001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RestrictedMemberReader.java#16 $
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) 2003-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    //
010    // jhyde, Feb 26, 2003
011    */
012    package mondrian.rolap;
013    
014    import java.util.ArrayList;
015    import java.util.Collections;
016    import java.util.List;
017    
018    import mondrian.olap.*;
019    import mondrian.rolap.sql.TupleConstraint;
020    import mondrian.rolap.sql.MemberChildrenConstraint;
021    
022    /**
023     * A <code>RestrictedMemberReader</code> reads only the members of a hierarchy
024     * allowed by a role's access profile.
025     *
026     * @author jhyde
027     * @since Feb 26, 2003
028     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RestrictedMemberReader.java#16 $
029     */
030    class RestrictedMemberReader extends DelegatingMemberReader {
031    
032        private final Role.HierarchyAccess hierarchyAccess;
033        private final boolean ragged;
034        private final SqlConstraintFactory sqlConstraintFactory =
035            SqlConstraintFactory.instance();
036    
037        /**
038         * Creates a <code>RestrictedMemberReader</code>.
039         *
040         * <p>There's no filtering to be done unless
041         * either the role has restrictions on this hierarchy,
042         * or the hierarchy is ragged; there's a pre-condition to this effect.</p>
043         *
044         * @param memberReader Underlying (presumably unrestricted) member reader
045         * @param role Role whose access profile to obey. The role must have
046         *   restrictions on this hierarchy
047         * @pre role.getAccessDetails(memberReader.getHierarchy()) != null ||
048         *   memberReader.getHierarchy().isRagged()
049         */
050        RestrictedMemberReader(MemberReader memberReader, Role role) {
051            super(memberReader);
052            RolapHierarchy hierarchy = memberReader.getHierarchy();
053            ragged = hierarchy.isRagged();
054            if (role.getAccessDetails(hierarchy) == null) {
055                assert ragged;
056                hierarchyAccess = RoleImpl.createAllAccess(hierarchy);
057            } else {
058                hierarchyAccess = role.getAccessDetails(hierarchy);
059            }
060        }
061    
062        public boolean setCache(MemberCache cache) {
063            // Don't support cache-writeback. It would confuse the cache!
064            return false;
065        }
066    
067        public RolapMember getLeadMember(RolapMember member, int n) {
068            int i = 0;
069            int increment = 1;
070            if (n < 0) {
071                increment = -1;
072                n = -n;
073            }
074            while (i < n) {
075                member = memberReader.getLeadMember(member, increment);
076                if (member.isNull()) {
077                    return member;
078                }
079                if (canSee(member)) {
080                    ++i;
081                }
082            }
083            return member;
084        }
085    
086        public void getMemberChildren(
087            RolapMember parentMember,
088            List<RolapMember> children)
089        {
090            MemberChildrenConstraint constraint =
091                sqlConstraintFactory.getMemberChildrenConstraint(null);
092            getMemberChildren(parentMember, children, constraint);
093        }
094    
095        public void getMemberChildren(
096            RolapMember parentMember,
097            List<RolapMember> children,
098            MemberChildrenConstraint constraint)
099        {
100            List<RolapMember> fullChildren = new ArrayList<RolapMember>();
101            memberReader.getMemberChildren(parentMember, fullChildren, constraint);
102            processMemberChildren(fullChildren, children, constraint);
103        }
104    
105        private void processMemberChildren(
106            List<RolapMember> fullChildren,
107            List<RolapMember> children,
108            MemberChildrenConstraint constraint)
109        {
110            // todo: optimize if parentMember is beyond last level
111            List<RolapMember> grandChildren = null;
112            for (int i = 0; i < fullChildren.size(); i++) {
113                RolapMember member = fullChildren.get(i);
114                // If a child is hidden (due to raggedness) include its children.
115                // This must be done before applying access-control.
116                if (ragged) {
117                    if (member.isHidden()) {
118                        // Replace this member with all of its children.
119                        // They might be hidden too, but we'll get to them in due
120                        // course. They also might be access-controlled; that's why
121                        // we deal with raggedness before we apply access-control.
122                        fullChildren.remove(i);
123                        if (grandChildren == null) {
124                            grandChildren = new ArrayList<RolapMember>();
125                        } else {
126                            grandChildren.clear();
127                        }
128                        memberReader.getMemberChildren(member, grandChildren, constraint);
129                        fullChildren.addAll(i, grandChildren);
130                        // Step back to before the first child we just inserted,
131                        // and go through the loop again.
132                        --i;
133                        continue;
134                    }
135                }
136                // Filter out children which are invisible because of
137                // access-control.
138                final Access access;
139                if (hierarchyAccess != null) {
140                    access = hierarchyAccess.getAccess(member);
141                } else {
142                    access = Access.ALL;
143                }
144                switch (access) {
145                case NONE:
146                    break;
147                default:
148                    children.add(member);
149                    break;
150                }
151            }
152        }
153    
154        /**
155         * Writes to members which we can see.
156         * @param members Input list
157         * @param filteredMembers Output list
158         */
159        private void filterMembers(
160            List<RolapMember> members,
161            List<RolapMember> filteredMembers)
162        {
163            for (RolapMember member : members) {
164                if (canSee(member)) {
165                    filteredMembers.add(member);
166                }
167            }
168        }
169    
170        private boolean canSee(final RolapMember member) {
171            if (ragged && member.isHidden()) {
172                return false;
173            }
174            if (hierarchyAccess != null) {
175                final Access access = hierarchyAccess.getAccess(member);
176                return access != Access.NONE;
177            }
178            return true;
179        }
180    
181        public void getMemberChildren(
182            List<RolapMember> parentMembers,
183            List<RolapMember> children)
184        {
185            MemberChildrenConstraint constraint =
186                sqlConstraintFactory.getMemberChildrenConstraint(null);
187            getMemberChildren(parentMembers, children, constraint);
188        }
189    
190        public synchronized void getMemberChildren(
191            List<RolapMember> parentMembers,
192            List<RolapMember> children,
193            MemberChildrenConstraint constraint)
194        {
195    //        for (Iterator i = parentMembers.iterator(); i.hasNext();) {
196    //            RolapMember parentMember = (RolapMember) i.next();
197    //            getMemberChildren(parentMember, children, constraint);
198    //        }
199            List<RolapMember> fullChildren = new ArrayList<RolapMember>();
200            super.getMemberChildren(parentMembers, fullChildren, constraint);
201            processMemberChildren(fullChildren, children, constraint);
202        }
203    
204        public List<RolapMember> getRootMembers() {
205            int topLevelDepth = hierarchyAccess.getTopLevelDepth();
206            if (topLevelDepth > 0) {
207                RolapLevel topLevel =
208                    (RolapLevel) getHierarchy().getLevels()[topLevelDepth];
209                return getMembersInLevel(topLevel, 0, Integer.MAX_VALUE);
210            }
211            return super.getRootMembers();
212        }
213    
214        public List<RolapMember> getMembersInLevel(
215            RolapLevel level,
216            int startOrdinal,
217            int endOrdinal)
218        {
219            TupleConstraint constraint =
220                sqlConstraintFactory.getLevelMembersConstraint(null);
221            return getMembersInLevel(level, startOrdinal, endOrdinal, constraint);
222        }
223    
224        public List<RolapMember> getMembersInLevel(
225            RolapLevel level,
226            int startOrdinal,
227            int endOrdinal,
228            TupleConstraint constraint)
229        {
230            if (hierarchyAccess != null) {
231                final int depth = level.getDepth();
232                if (depth < hierarchyAccess.getTopLevelDepth()) {
233                    return Collections.emptyList();
234                }
235                if (depth > hierarchyAccess.getBottomLevelDepth()) {
236                    return Collections.emptyList();
237                }
238            }
239            final List<RolapMember> membersInLevel =
240                memberReader.getMembersInLevel(
241                    level, startOrdinal, endOrdinal, constraint);
242            List<RolapMember> filteredMembers = new ArrayList<RolapMember>();
243            filterMembers(membersInLevel, filteredMembers);
244            return filteredMembers;
245        }
246    
247        public RolapMember getDefaultMember() {
248            RolapMember defaultMember =
249                (RolapMember) getHierarchy().getDefaultMember();
250            if (defaultMember != null) {
251                Access i = hierarchyAccess.getAccess(defaultMember);
252                if (i != Access.NONE) {
253                    return defaultMember;
254                }
255            }
256            return getRootMembers().get(0);
257        }
258    
259        public RolapMember getMemberParent(RolapMember member) {
260            RolapMember parentMember = member.getParentMember();
261            // Skip over hidden parents.
262            while (parentMember != null && parentMember.isHidden()) {
263                parentMember = parentMember.getParentMember();
264            }
265            // Skip over non-accessible parents.
266            if (parentMember != null) {
267                if (hierarchyAccess.getAccess(parentMember) == Access.NONE) {
268                    return null;
269                }
270            }
271            return parentMember;
272        }
273    }
274    
275    // End RestrictedMemberReader.java