001 /*
002 // $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RestrictedMemberReader.java#2 $
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-release/3.0/src/main/mondrian/rolap/RestrictedMemberReader.java#2 $
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