001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/MemberCacheHelper.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) 2001-2002 Kana Software, Inc.
007    // Copyright (C) 2001-2008 Julian Hyde and others
008    // Copyright (C) 2004-2005 TONBELLER AG
009    // All Rights Reserved.
010    // You must accept the terms of that agreement to use this software.
011    //
012    // jhyde, 21 December, 2001
013    */
014    package mondrian.rolap;
015    
016    import mondrian.rolap.cache.SmartCache;
017    import mondrian.rolap.cache.SoftSmartCache;
018    import mondrian.rolap.sql.MemberChildrenConstraint;
019    import mondrian.rolap.sql.TupleConstraint;
020    import mondrian.spi.DataSourceChangeListener;
021    import mondrian.olap.Util;
022    
023    import java.util.*;
024    
025    /**
026     * Encapsulation of member caching.
027     *
028     * @author Will Gorman (wgorman@pentaho.org)
029     * @version $Id: //open/mondrian/src/main/mondrian/rolap/MemberCacheHelper.java#3 $
030     */
031    public class MemberCacheHelper implements MemberCache {
032    
033        private final SqlConstraintFactory sqlConstraintFactory =
034            SqlConstraintFactory.instance();
035    
036        /** maps a parent member to a list of its children */
037        final SmartMemberListCache<RolapMember, List<RolapMember>>
038            mapMemberToChildren;
039    
040        /** a cache for alle members to ensure uniqueness */
041        SmartCache<Object, RolapMember> mapKeyToMember;
042        RolapHierarchy rolapHierarchy;
043        DataSourceChangeListener changeListener;
044    
045        /** maps a level to its members */
046        final SmartMemberListCache<RolapLevel, List<RolapMember>>
047            mapLevelToMembers;
048    
049        /**
050         * Creates a MemberCacheHelper.
051         *
052         * @param rolapHierarchy Hierarchy
053         */
054        public MemberCacheHelper(RolapHierarchy rolapHierarchy) {
055            this.rolapHierarchy = rolapHierarchy;
056            this.mapLevelToMembers =
057                new SmartMemberListCache<RolapLevel, List<RolapMember>>();
058            this.mapKeyToMember =
059                new SoftSmartCache<Object, RolapMember>();
060            this.mapMemberToChildren =
061                new SmartMemberListCache<RolapMember, List<RolapMember>>();
062    
063            if (rolapHierarchy != null) {
064                changeListener =
065                    rolapHierarchy.getRolapSchema().getDataSourceChangeListener();
066            } else {
067                changeListener = null;
068            }
069        }
070    
071        // implement MemberCache
072        // synchronization: Must synchronize, because uses mapKeyToMember
073        public synchronized RolapMember getMember(
074            Object key,
075            boolean mustCheckCacheStatus)
076        {
077            if (mustCheckCacheStatus) {
078                checkCacheStatus();
079            }
080            return mapKeyToMember.get(key);
081        }
082    
083    
084        // implement MemberCache
085        // synchronization: Must synchronize, because modifies mapKeyToMember
086        public synchronized Object putMember(Object key, RolapMember value) {
087            return mapKeyToMember.put(key, value);
088        }
089    
090        // implement MemberCache
091        public Object makeKey(RolapMember parent, Object key) {
092            return new MemberKey(parent, key);
093        }
094    
095        // implement MemberCache
096        // synchronization: Must synchronize, because modifies mapKeyToMember
097        public synchronized RolapMember getMember(Object key) {
098            return getMember(key, true);
099        }
100    
101        public synchronized void checkCacheStatus() {
102            if (changeListener != null) {
103                if (changeListener.isHierarchyChanged(rolapHierarchy)) {
104                    flushCache();
105                }
106            }
107        }
108    
109        /**
110         * ???
111         *
112         * @param level
113         * @param constraint
114         * @param members
115         */
116        // synchronization: Must synchronize, because modifies mapKeyToMember
117        public synchronized void putLevelMembersInCache(
118            RolapLevel level,
119            TupleConstraint constraint,
120            List<RolapMember> members)
121        {
122            mapLevelToMembers.put(level, constraint, members);
123        }
124    
125        public synchronized List<RolapMember> getChildrenFromCache(
126            RolapMember member,
127            MemberChildrenConstraint constraint)
128        {
129            if (constraint == null) {
130                constraint =
131                    sqlConstraintFactory.getMemberChildrenConstraint(null);
132            }
133            return mapMemberToChildren.get(member, constraint);
134        }
135    
136        public synchronized void putChildren(
137            RolapMember member,
138            MemberChildrenConstraint constraint,
139            List<RolapMember> children)
140        {
141            if (constraint == null) {
142                constraint =
143                    sqlConstraintFactory.getMemberChildrenConstraint(null);
144            }
145            mapMemberToChildren.put(member, constraint, children);
146        }
147    
148        public synchronized List<RolapMember> getLevelMembersFromCache(
149            RolapLevel level,
150            TupleConstraint constraint)
151        {
152            if (constraint == null) {
153                constraint = sqlConstraintFactory.getLevelMembersConstraint(null);
154            }
155            return mapLevelToMembers.get(level, constraint);
156        }
157    
158        public synchronized void flushCache() {
159            mapMemberToChildren.clear();
160            mapKeyToMember.clear();
161            mapLevelToMembers.clear();
162        }
163    
164        public DataSourceChangeListener getChangeListener() {
165            return changeListener;
166        }
167    
168        public void setChangeListener(DataSourceChangeListener listener) {
169            changeListener = listener;
170        }
171    
172        public boolean isMutable()
173        {
174            return true;
175        }
176    
177        public synchronized RolapMember removeMember(Object key)
178        {
179            RolapMember member = getMember(key);
180            if (member == null) {
181                // not in cache
182                return null;
183            }
184    
185            // Drop member from the level-to-members map.
186            // Cache key is a (level, constraint) pair,
187            // cache value is a list of RolapMember.
188            // For each cache key whose level matches, remove from the list,
189            // regardless of the constraint.
190            RolapLevel level = member.getLevel();
191            for (Map.Entry<
192                SmartMemberListCache.Key2<RolapLevel, Object>,
193                List<RolapMember>> entry : mapLevelToMembers.getCache())
194            {
195                if (entry.getKey().o1.equals(level)) {
196                    List<RolapMember> peers = entry.getValue();
197                    boolean removedIt = peers.remove(member);
198                    Util.discard(removedIt);
199                }
200            }
201    
202            // Drop member from the member-to-children map, wherever it occurs as
203            // a parent or as a child, regardless of the constraint.
204            RolapMember parent = member.getParentMember();
205            final Iterator<
206                Map.Entry<
207                    SmartMemberListCache.Key2<RolapMember, Object>,
208                    List<RolapMember>>> iter =
209                mapMemberToChildren.getCache().iterator();
210            while (iter.hasNext()) {
211                Map.Entry<
212                    SmartMemberListCache.Key2<RolapMember, Object>,
213                    List<RolapMember>> entry = iter.next();
214                final RolapMember member1 = entry.getKey().o1;
215                final Object constraint = entry.getKey().o2;
216    
217                // Cache key is (member's parent, constraint);
218                // cache value is a list of member's siblings;
219                // If constraint is trivial remove member from list of siblings;
220                // otherwise it's safer to nuke the cache entry
221                if (member1.equals(parent)) {
222                    if (constraint == DefaultMemberChildrenConstraint.instance()) {
223                        List<RolapMember> siblings = entry.getValue();
224                        boolean removedIt = siblings.remove(member);
225                        Util.discard(removedIt);
226                    } else {
227                        iter.remove();
228                    }
229                }
230    
231                // cache is (member, some constraint);
232                // cache value is list of member's children;
233                // remove cache entry
234                if (member1.equals(member)) {
235                    iter.remove();
236                }
237            }
238    
239            // drop it from the lookup-cache
240            return mapKeyToMember.put(key, null);
241        }
242    
243        public synchronized RolapMember removeMemberAndDescendants(Object key) {
244            // Can use mapMemberToChildren recursively. No need to update inferior
245            // lists of children. Do need to update inferior lists of level-peers.
246            return null; // STUB
247        }
248    
249    }
250    
251    // End MemberCacheHelper.java