smartmemberreader.java

来自「数据仓库展示程序」· Java 代码 · 共 576 行 · 第 1/2 页

JAVA
576
字号
/*
// $Id: //open/mondrian/src/main/mondrian/rolap/SmartMemberReader.java#26 $
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// (C) Copyright 2001-2005 Kana Software, Inc. and others.
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 21 December, 2001
*/

package mondrian.rolap;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import mondrian.olap.Util;

/**
 * <code>SmartMemberReader</code> implements {@link MemberReader} by keeping a
 * cache of members and their children. If a member is 'in cache', there is a
 * list of its children. Its children are not necessarily 'in cache'.
 *
 * <p>Cache retention. We have not implemented a cache flushing policy. Members
 * are never removed from the cache.</p>
 *
 * <p>Uniqueness. We need to ensure that there is never more than one {@link
 * RolapMember} object representing the same member.</p>
 *
 * @author jhyde
 * @since 21 December, 2001
 * @version $Id: //open/mondrian/src/main/mondrian/rolap/SmartMemberReader.java#26 $
 **/
public class SmartMemberReader implements MemberReader, MemberCache {
    private final MemberReader source;
    /** Maps {@link RolapMember} to a {@link ChildrenList} of its children, and
     * records usages.
     * Locking strategy is to lock the parent SmartMemberReader. **/
    private final Map mapMemberToChildren;
    /** Maps a {@link MemberKey} to a {@link SoftReference} to a
     * {@link RolapMember}, and is used to ensure that there is at most one
     * object representing a given member.
     * The soft reference allows members to be forgotten.
     * Locking strategy is to lock the parent SmartMemberReader. **/
    private final Map mapKeyToMember;
    private List rootMembers;
    private final Map mapLevelToMembers;

    SmartMemberReader(MemberReader source) {
        this.source = source;
        if (!source.setCache(this)) {
            throw Util.newInternal(
                    "MemberSource (" + source + ", " + source.getClass() +
                    ") does not support cache-writeback");
        }
        this.mapLevelToMembers = new HashMap();
        this.mapKeyToMember = new HashMap();
        this.mapMemberToChildren = new HashMap();
    }

    // implement MemberReader
    public RolapHierarchy getHierarchy() {
        return source.getHierarchy();
    }

    // implement MemberSource
    public boolean setCache(MemberCache cache) {
        // we do not support cache writeback -- we must be masters of our
        // own cache
        return false;
    }

    // implement MemberCache
    public Object makeKey(RolapMember parent, Object key) {
        return new MemberKey(parent, key);
    }

    // implement MemberCache
    // synchronization: Must synchronize, because uses mapKeyToMember
    public synchronized RolapMember getMember(Object key) {
        SoftReference ref = (SoftReference) mapKeyToMember.get(key);
        if (ref == null) {
            return null;
        }
        final RolapMember rolapMember = (RolapMember) ref.get();
        if (rolapMember == null) {
            // Referenced member has been garbage collected; remove the hash
            // table entry too.
            mapKeyToMember.put(key,null);
        }
        return rolapMember;
    }

    // implement MemberCache
    // synchronization: Must synchronize, because modifies mapKeyToMember
    public synchronized Object putMember(Object key, RolapMember value) {
        return mapKeyToMember.put(key, new SoftReference(value));
    }

    // implement MemberReader
    public RolapMember[] getMembers() {
        List v = new ArrayList();
        RolapLevel[] levels = (RolapLevel[]) getHierarchy().getLevels();
        // todo: optimize by walking to children for members we know about
        for (int i = 0; i < levels.length; i++) {
            getMembersInLevel(v, levels[i], 0, Integer.MAX_VALUE);
        }
        return RolapUtil.toArray(v);
    }

    public List getRootMembers() {
        if (rootMembers == null) {
            rootMembers = source.getRootMembers();
        }
        return rootMembers;
    }


    /**
     * @synchronization modifies mapLevelToMembers
     */
    public synchronized List getMembersInLevel(RolapLevel level, 
                                               int startOrdinal, 
                                               int endOrdinal) {
        SoftReference ref = (SoftReference) mapLevelToMembers.get(level);
        if (ref != null) {
            List members = (List) ref.get();
            if (members != null) {
                return members;
            }
            mapLevelToMembers.remove(level);
        }
        List members = source.getMembersInLevel(level, startOrdinal, endOrdinal);
        ref = new SoftReference(members);
        mapLevelToMembers.put(level, ref);
        return members;
    }

    private void getMembersInLevel(List result, 
                                   RolapLevel level, 
                                   int startOrdinal, 
                                   int endOrdinal) {
        final List membersInLevel = 
            getMembersInLevel(level, startOrdinal, endOrdinal);
        result.addAll(membersInLevel);
    }

    public void getMemberChildren(RolapMember parentMember, List children) {
        List parentMembers = new ArrayList();
        parentMembers.add(parentMember);
        getMemberChildren(parentMembers, children);
    }

    public synchronized void getMemberChildren(List parentMembers, 
                                               List children) {
        List missed = new ArrayList();
        for (Iterator it = parentMembers.iterator(); it.hasNext();) {
            RolapMember parent = (RolapMember) it.next();
            SoftReference ref = (SoftReference)
                    mapMemberToChildren.get(parent);
            if (ref == null) {
                missed.add(parent);
            } else {
                ChildrenList v = (ChildrenList) ref.get();
                if (v == null) {
                    mapMemberToChildren.remove(parent);
                    missed.add(parent);
                } else {
                    children.addAll(v.list);
                }
            }
        }
        if (missed.size() > 0) {
            readMemberChildren(missed, children);
        }
    }

    public RolapMember lookupMember(String[] uniqueNameParts, 
                                    boolean failIfNotFound) {
        return RolapUtil.lookupMember(this, uniqueNameParts, failIfNotFound);
    }

    /**
     * A <code>ChildrenList</code> is held in the
     * {@link SmartMemberReader#mapMemberToChildren} cache.
     **/
    private static class ChildrenList {
        private final RolapMember member;
        private final List list;

        ChildrenList(RolapMember member, List list) {
            this.member = member;
            this.list = list;
        }

        public String toString() {
            return super.toString() 
                + " {member=" 
                + member 
                + ", childCount=" 
                + list.size() 
                + "}";
        }

    };

    /**
     * Reads the children of <code>member</code> into cache, and also into
     * <code>result</code>.
     *
     * @param result Children are written here, in order
     * @param members Members whose children to read
     **/
    private void readMemberChildren(List members, List result) {
        if (false) {
            // Pre-condition disabled. It makes sense to have the pre-
            // condition, because lists of parent members are typically
            // sorted by construction, and we should be able to exploit this
            // when constructing the (significantly larger) set of children.
            // But currently BasicQueryTest.testBasketAnalysis() fails this
            // assert, and I haven't had time to figure out why.
            //   -- jhyde, 2004/6/10.
            Util.assertPrecondition(isSorted(members), "isSorted(members)");
        }
        List children = new ArrayList();
        source.getMemberChildren(members, children);
        // Put them in a temporary hash table first. Register them later, when
        // we know their size (hence their 'cost' to the cache pool).
        Map tempMap = new HashMap();
        for (int i = 0, n = members.size(); i < n; i++) {
            tempMap.put(members.get(i), new ArrayList());
        }
        for (int i = 0, childrenCount = children.size(); i < childrenCount; i++) {
            // todo: We could optimize here. If members.length is small, it's
            // more efficient to drive from members, rather than hashing
            // children.length times. We could also exploit the fact that the
            // result is sorted by ordinal and therefore, unless the "members"
            // contains members from different levels, children of the same
            // member will be contiguous.
            RolapMember child = (RolapMember) children.get(i);
            List list = (ArrayList) tempMap.get(child.getParentMember());
            list.add(child);
            result.add(child);
        }
        synchronized (this) {
            for (Iterator keys = tempMap.keySet().iterator(); keys.hasNext();) {
                RolapMember member = (RolapMember) keys.next();
                List list = (ArrayList) tempMap.get(member);
                if (!hasChildren(member)) {
                    putChildren(member, list);
                }
            }
        }
    }

    /**
     * Returns true if every element of <code>members</code> is not null and is
     * strictly less than the following element; false otherwise.
     */
    public boolean isSorted(List members) {
        final int count = members.size();
        if (count == 0) {
            return true;
        }
        RolapMember m1 = (RolapMember) members.get(0);
        if (m1 == null) {
            // Special case check for 0th element, just in case length == 1.
            return false;
        }
        for (int i = 1; i < count; i++) {
            RolapMember m0 = m1;
            m1 = (RolapMember) members.get(i);
            if (m1 == null || compare(m0, m1, false) >= 0) {
                return false;
            }
        }
        return true;
    }

    public synchronized boolean hasChildren(RolapMember member) {
        return mapMemberToChildren.get(member) != null;
    }

    public synchronized boolean hasLevelMembers(RolapLevel level) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?