001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2001-2002 Julian Hyde
008// Copyright (C) 2004-2005 TONBELLER AG
009// Copyright (C) 2007-2008 StrateBI
010// Copyright (C) 2008-2012 Pentaho and others
011// All Rights Reserved.
012*/
013package mondrian.rolap;
014
015import mondrian.olap.*;
016import mondrian.rolap.TupleReader.MemberBuilder;
017import mondrian.rolap.sql.MemberChildrenConstraint;
018import mondrian.rolap.sql.TupleConstraint;
019import mondrian.util.ConcatenableList;
020
021import org.apache.log4j.Logger;
022
023import java.util.*;
024
025/**
026 * <code>NoCacheMemberReader</code> implements {@link MemberReader} but
027 * without doing any kind of caching and avoiding to read all members.
028 *
029 * @author jlopez, lcanals
030 * @since 06 October, 2007
031 */
032public class NoCacheMemberReader implements MemberReader, MemberCache {
033    private static final Logger LOGGER =
034        Logger.getLogger(NoCacheMemberReader.class);
035
036    private final SqlConstraintFactory sqlConstraintFactory =
037        SqlConstraintFactory.instance();
038
039    private final MemberReader source;
040
041
042    NoCacheMemberReader(MemberReader source) {
043        this.source = source;
044        if (!source.setCache(this)) {
045            throw Util.newInternal(
046                "MemberSource (" + source + ", " + source.getClass()
047                + ") does not support cache-writeback");
048        }
049    }
050
051    // implementes MemberCache
052    public boolean isMutable() {
053        return false;
054    }
055
056    // implementes MemberCache
057    public RolapMember removeMember(Object key) {
058        return null;
059    }
060
061    // implementes MemberCache
062    public RolapMember removeMemberAndDescendants(Object key) {
063        return null;
064    }
065
066    // implement MemberReader
067    public RolapHierarchy getHierarchy() {
068        return source.getHierarchy();
069    }
070
071    // implement MemberCache
072    public boolean setCache(MemberCache cache) {
073        return false;
074    }
075
076    // implement MemberCache
077    public Object makeKey(final RolapMember parent, final Object key) {
078        LOGGER.debug("Entering makeKey");
079        return new MemberKey(parent, key);
080    }
081
082    public synchronized RolapMember getMember(final Object key) {
083        return getMember(key, true);
084    }
085
086    public RolapMember getMember(
087        final Object key,
088        final boolean mustCheckCacheStatus)
089    {
090        LOGGER.debug("Returning null member: no cache");
091        return null;
092    }
093
094
095    // implement MemberCache
096    public Object putMember(final Object key, final RolapMember value) {
097        LOGGER.debug("putMember void for no caching");
098        return value;
099    }
100
101    // implement MemberReader
102    public List<RolapMember> getMembers() {
103        System.out.println("NoCache getMembers");
104        List<RolapMember> v = new ArrayList<RolapMember>();
105        RolapLevel[] levels = (RolapLevel[]) getHierarchy().getLevels();
106        // todo: optimize by walking to children for members we know about
107        for (RolapLevel level : levels) {
108            List<RolapMember> membersInLevel =
109                getMembersInLevel(level);
110            v.addAll(membersInLevel);
111        }
112        return v;
113    }
114
115    public List<RolapMember> getRootMembers() {
116        LOGGER.debug("Getting root members");
117        return source.getRootMembers();
118    }
119
120    public List<RolapMember> getMembersInLevel(
121        final RolapLevel level)
122    {
123        TupleConstraint constraint =
124            sqlConstraintFactory.getLevelMembersConstraint(null);
125        return getMembersInLevel(level, constraint);
126    }
127
128    public List<RolapMember> getMembersInLevel(
129        final RolapLevel level, final TupleConstraint constraint)
130    {
131        LOGGER.debug("Entering getMembersInLevel");
132        return source.getMembersInLevel(
133            level, constraint);
134    }
135
136    public RolapMember getMemberByKey(
137        RolapLevel level, List<Comparable> keyValues)
138    {
139        return source.getMemberByKey(level, keyValues);
140    }
141
142    public void getMemberChildren(
143        final RolapMember parentMember,
144        final List<RolapMember> children)
145    {
146        MemberChildrenConstraint constraint =
147                sqlConstraintFactory.getMemberChildrenConstraint(null);
148        getMemberChildren(parentMember, children, constraint);
149    }
150
151    public Map<? extends Member, Access> getMemberChildren(
152        final RolapMember parentMember,
153        final List<RolapMember> children,
154        final MemberChildrenConstraint constraint)
155    {
156        List<RolapMember> parentMembers = new ArrayList<RolapMember>();
157        parentMembers.add(parentMember);
158        return getMemberChildren(parentMembers, children, constraint);
159    }
160
161    public void getMemberChildren(
162        final List<RolapMember> parentMembers,
163        final List<RolapMember> children)
164    {
165        MemberChildrenConstraint constraint =
166            sqlConstraintFactory.getMemberChildrenConstraint(null);
167        getMemberChildren(parentMembers, children, constraint);
168    }
169
170    public Map<? extends Member, Access> getMemberChildren(
171        final List<RolapMember> parentMembers,
172        final List<RolapMember> children,
173        final MemberChildrenConstraint constraint)
174    {
175        assert (constraint != null);
176        LOGGER.debug("Entering getMemberChildren");
177        return
178            source.getMemberChildren(
179                parentMembers, children, constraint);
180    }
181
182    public RolapMember lookupMember(
183        final List<Id.Segment> uniqueNameParts,
184        final boolean failIfNotFound)
185    {
186        return RolapUtil.lookupMember(this, uniqueNameParts, failIfNotFound);
187    }
188
189    public List<RolapMember> getChildrenFromCache(
190        final RolapMember member,
191        final MemberChildrenConstraint constraint)
192    {
193        return null;
194    }
195
196    public List<RolapMember> getLevelMembersFromCache(
197        final RolapLevel level,
198        final TupleConstraint constraint)
199    {
200        return null;
201    }
202
203    public void putChildren(
204        final RolapMember member,
205        final MemberChildrenConstraint constraint,
206        final List<RolapMember> children)
207    {
208    }
209
210    public void putChildren(
211        final RolapLevel level,
212        final TupleConstraint constraint,
213        final List<RolapMember> children)
214    {
215    }
216
217    public RolapMember getLeadMember(RolapMember member, int n) {
218        if (n == 0 || member.isNull()) {
219            return member;
220        } else {
221            SiblingIterator iter = new SiblingIterator(this, member);
222            if (n > 0) {
223                RolapMember sibling = null;
224                while (n-- > 0) {
225                    if (!iter.hasNext()) {
226                        return (RolapMember) member.getHierarchy()
227                            .getNullMember();
228                    }
229                    sibling = iter.nextMember();
230                }
231                return sibling;
232            } else {
233                n = -n;
234                RolapMember sibling = null;
235                while (n-- > 0) {
236                    if (!iter.hasPrevious()) {
237                        return (RolapMember) member.getHierarchy()
238                            .getNullMember();
239                    }
240                    sibling = iter.previousMember();
241                }
242                return sibling;
243            }
244        }
245    }
246
247    public void getMemberRange(
248        final RolapLevel level,
249        final RolapMember startMember,
250        final RolapMember endMember,
251        final List<RolapMember> list)
252    {
253        assert startMember != null : "pre";
254        assert endMember != null : "pre";
255        assert startMember.getLevel() == endMember.getLevel()
256            : "pre: startMember.getLevel() == endMember.getLevel()";
257
258        if (compare(startMember, endMember, false) > 0) {
259            return;
260        }
261        list.add(startMember);
262        if (startMember.equals(endMember)) {
263            return;
264        }
265        SiblingIterator siblings = new SiblingIterator(this, startMember);
266        while (siblings.hasNext()) {
267            final RolapMember member = siblings.nextMember();
268            list.add(member);
269            if (member.equals(endMember)) {
270                return;
271            }
272        }
273        throw Util.newInternal(
274            "sibling iterator did not hit end point, start="
275            + startMember + ", end=" + endMember);
276    }
277
278    public int getMemberCount() {
279        return source.getMemberCount();
280    }
281
282    public int compare(
283        final RolapMember m1,
284        final RolapMember m2,
285        final boolean siblingsAreEqual)
286    {
287        if (Util.equals(m1, m2)) {
288            return 0;
289        }
290        if (Util.equals(m1.getParentMember(), m2.getParentMember())) {
291            // including case where both parents are null
292            if (siblingsAreEqual) {
293                return 0;
294            } else if (m1.getParentMember() == null) {
295                // at this point we know that both parent members are null.
296                int pos1 = -1, pos2 = -1;
297                List<RolapMember> siblingList = getRootMembers();
298                for (int i = 0, n = siblingList.size(); i < n; i++) {
299                    RolapMember child = siblingList.get(i);
300                    if (child.equals(m1)) {
301                        pos1 = i;
302                    }
303                    if (child.equals(m2)) {
304                        pos2 = i;
305                    }
306                }
307                if (pos1 == -1) {
308                    throw Util.newInternal(m1 + " not found among siblings");
309                }
310                if (pos2 == -1) {
311                    throw Util.newInternal(m2 + " not found among siblings");
312                }
313                Util.assertTrue(pos1 != pos2);
314                return pos1 < pos2 ? -1 : 1;
315            } else {
316                List<RolapMember> children = new ArrayList<RolapMember>();
317                getMemberChildren(m1.getParentMember(), children);
318                int pos1 = -1, pos2 = -1;
319                for (int i = 0, n = children.size(); i < n; i++) {
320                    RolapMember child = children.get(i);
321                    if (child.equals(m1)) {
322                        pos1 = i;
323                    }
324                    if (child.equals(m2)) {
325                        pos2 = i;
326                    }
327                }
328                if (pos1 == -1) {
329                    throw Util.newInternal(m1 + " not found among siblings");
330                }
331                if (pos2 == -1) {
332                    throw Util.newInternal(m2 + " not found among siblings");
333                }
334                assert pos1 != pos2;
335                return pos1 < pos2 ? -1 : 1;
336            }
337        }
338        int levelDepth1 = m1.getLevel().getDepth();
339        int levelDepth2 = m2.getLevel().getDepth();
340        if (levelDepth1 < levelDepth2) {
341            final int c = compare(m1, m2.getParentMember(), false);
342            return (c == 0) ? -1 : c;
343
344        } else if (levelDepth1 > levelDepth2) {
345            final int c = compare(m1.getParentMember(), m2, false);
346            return (c == 0) ? 1 : c;
347
348        } else {
349            return compare(m1.getParentMember(), m2.getParentMember(), false);
350        }
351    }
352
353    /**
354     * <code>SiblingIterator</code> helps traverse a hierarchy of members, by
355     * remembering the position at each level. Each SiblingIterator has a
356     * parent, to which it defers when the last child of the current member is
357     * reached.
358     */
359    class SiblingIterator {
360        private final MemberReader reader;
361        private final SiblingIterator parentIterator;
362        private List<? extends Member> siblings;
363        private int position;
364
365        SiblingIterator(MemberReader reader, RolapMember member) {
366            this.reader = reader;
367            RolapMember parent = member.getParentMember();
368            List<RolapMember> siblingList;
369            if (parent == null) {
370                siblingList = reader.getRootMembers();
371                this.parentIterator = null;
372            } else {
373                siblingList = new ArrayList<RolapMember>();
374                reader.getMemberChildren(parent, siblingList);
375                this.parentIterator = new SiblingIterator(reader, parent);
376            }
377            this.siblings = siblingList;
378            this.position = -1;
379            for (int i = 0; i < this.siblings.size(); i++) {
380                if (siblings.get(i).equals(member)) {
381                    this.position = i;
382                    break;
383                }
384            }
385            if (this.position == -1) {
386                throw Util.newInternal(
387                    "member " + member + " not found among its siblings");
388            }
389        }
390
391        boolean hasNext() {
392            return (this.position < this.siblings.size() - 1)
393                || (parentIterator != null)
394                && parentIterator.hasNext();
395        }
396
397        Object next() {
398            return nextMember();
399        }
400
401        RolapMember nextMember() {
402            if (++this.position >= this.siblings.size()) {
403                if (parentIterator == null) {
404                    throw Util.newInternal("there is no next member");
405                }
406                RolapMember parent = parentIterator.nextMember();
407                List<RolapMember> siblingList = new ArrayList<RolapMember>();
408                reader.getMemberChildren(parent, siblingList);
409                this.siblings = siblingList;
410                this.position = 0;
411            }
412            return (RolapMember) this.siblings.get(this.position);
413        }
414
415        boolean hasPrevious() {
416            return (this.position > 0)
417                || (parentIterator != null)
418                && parentIterator.hasPrevious();
419        }
420
421        Object previous() {
422            return previousMember();
423        }
424
425        RolapMember previousMember() {
426            if (--this.position < 0) {
427                if (parentIterator == null) {
428                    throw Util.newInternal("there is no next member");
429                }
430                RolapMember parent = parentIterator.previousMember();
431                List<RolapMember> siblingList = new ArrayList<RolapMember>();
432                reader.getMemberChildren(parent, siblingList);
433                this.siblings = siblingList;
434                this.position = this.siblings.size() - 1;
435            }
436            return (RolapMember) this.siblings.get(this.position);
437        }
438    }
439
440    public MemberBuilder getMemberBuilder() {
441        return source.getMemberBuilder();
442    }
443
444    public RolapMember getDefaultMember() {
445        RolapMember defaultMember =
446            (RolapMember) getHierarchy().getDefaultMember();
447        if (defaultMember != null) {
448            return defaultMember;
449        }
450        return getRootMembers().get(0);
451    }
452
453    public int getLevelMemberCount(RolapLevel level) {
454        // No need to cache the result: the caller saves the result by calling
455        // RolapLevel.setApproxRowCount
456        return source.getLevelMemberCount(level);
457    }
458
459    public RolapMember desubstitute(RolapMember member) {
460        return member;
461    }
462
463    public RolapMember substitute(RolapMember member) {
464        return member;
465    }
466
467    public RolapMember getMemberParent(RolapMember member) {
468        // This method deals with ragged hierarchies but not access-controlled
469        // hierarchies - assume these have RestrictedMemberReader possibly
470        // wrapped in a SubstitutingMemberReader.
471        RolapMember parentMember = member.getParentMember();
472        // Skip over hidden parents.
473        while (parentMember != null && parentMember.isHidden()) {
474            parentMember = parentMember.getParentMember();
475        }
476        return parentMember;
477    }
478
479    /**
480     * Reads the children of <code>member</code> into <code>result</code>.
481     *
482     * @param result Children are written here, in order
483     * @param members Members whose children to read
484     * @param constraint restricts the returned members if possible (optional
485     *             optimization)
486     */
487    protected void readMemberChildren(
488        List<RolapMember> members,
489        List<RolapMember> result,
490        MemberChildrenConstraint constraint)
491    {
492        List<RolapMember> children = new ConcatenableList<RolapMember>();
493        source.getMemberChildren(members, children, constraint);
494        // Put them in a temporary hash table first. Register them later, when
495        // we know their size (hence their 'cost' to the cache pool).
496        Map<RolapMember, List<RolapMember>> tempMap =
497            new HashMap<RolapMember, List<RolapMember>>();
498        for (RolapMember member1 : members) {
499            //noinspection unchecked
500            tempMap.put(member1, Collections.EMPTY_LIST);
501        }
502        for (final RolapMember child : children) {
503            // todo: We could optimize here. If members.length is small, it's
504            // more efficient to drive from members, rather than hashing
505            // children.length times. We could also exploit the fact that the
506            // result is sorted by ordinal and therefore, unless the "members"
507            // contains members from different levels, children of the same
508            // member will be contiguous.
509            assert child != null : "child";
510            final RolapMember parentMember = child.getParentMember();
511            List<RolapMember> list = tempMap.get(parentMember);
512            if (list == null) {
513                // The list is null if, due to dropped constraints, we now
514                // have a children list of a member we didn't explicitly
515                // ask for it. Adding it to the cache would be viable, but
516                // let's ignore it.
517                continue;
518            } else if (list == Collections.EMPTY_LIST) {
519                list = new ArrayList<RolapMember>();
520                tempMap.put(parentMember, list);
521            }
522            list.add(child);
523            result.add(child);
524        }
525    }
526}
527
528// End NoCacheMemberReader.java