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) 2004-2005 TONBELLER AG
008// Copyright (C) 2005-2005 Julian Hyde
009// Copyright (C) 2005-2013 Pentaho and others
010// All Rights Reserved.
011*/
012package mondrian.rolap;
013
014import mondrian.calc.ResultStyle;
015import mondrian.olap.*;
016import mondrian.rolap.sql.TupleConstraint;
017
018import java.sql.SQLException;
019import java.util.*;
020
021 /**
022 * Helper class for {@link mondrian.rolap.HighCardSqlTupleReader} that
023 * keeps track of target levels and constraints for adding to SQL query.
024 *
025 * @deprecated Deprecated for Mondrian 4.0.
026 * @author luis f. canals, Kurtis Walker
027 * @since July 23, 2009
028 */
029@Deprecated
030public class Target extends TargetBase {
031    private final HighCardSqlTupleReader sqlTupleReader;
032    private final MemberCache cache;
033    private final TupleConstraint constraint;
034
035    boolean parentChild;
036    private RolapLevel[] levels;
037    private int levelDepth;
038
039    public Target(
040        final RolapLevel level,
041        final TupleReader.MemberBuilder memberBuilder,
042        final List<RolapMember> srcMembers,
043        final TupleConstraint constraint,
044        final HighCardSqlTupleReader sqlTupleReader)
045    {
046        super(srcMembers, level, memberBuilder);
047        this.sqlTupleReader = sqlTupleReader;
048        this.constraint = constraint;
049        this.cache = memberBuilder.getMemberCache();
050    }
051
052    public void open() {
053        levels = (RolapLevel[]) level.getHierarchy().getLevels();
054        setList(new ArrayList<RolapMember>());
055        levelDepth = level.getDepth();
056        parentChild = level.isParentChild();
057    }
058
059    int internalAddRow(SqlStatement stmt, int column)
060        throws SQLException
061    {
062        RolapMember member = null;
063        if (getCurrMember() != null) {
064            member = getCurrMember();
065        } else {
066            boolean checkCacheStatus = true;
067            final List<SqlStatement.Accessor> accessors = stmt.getAccessors();
068            for (int i = 0; i <= levelDepth; i++) {
069                RolapLevel childLevel = levels[i];
070                if (childLevel.isAll()) {
071                    member = memberBuilder.allMember();
072                    continue;
073                }
074
075                if (childLevel.isParentChild()) {
076                    column++;
077                }
078
079                Object value = accessors.get(column++).get();
080                if (value == null) {
081                    value = RolapUtil.sqlNullValue;
082                }
083                Object captionValue;
084                if (childLevel.hasCaptionColumn()) {
085                    captionValue = accessors.get(column++).get();
086                } else {
087                    captionValue = null;
088                }
089                RolapMember parentMember = member;
090                Object key = cache.makeKey(parentMember, value);
091                member = cache.getMember(key, checkCacheStatus);
092                checkCacheStatus = false; /* Only check the first time */
093                if (member == null) {
094                    if (constraint instanceof
095                        RolapNativeCrossJoin.NonEmptyCrossJoinConstraint
096                        && childLevel.isParentChild())
097                    {
098                        member = castToNonEmptyCJConstraint(constraint)
099                            .findMember(value);
100                    }
101                    if (member == null) {
102                        member = memberBuilder.makeMember(
103                            parentMember, childLevel, value, captionValue,
104                            parentChild, stmt, key, column);
105                    }
106                }
107
108                // Skip over the columns consumed by makeMember
109                if (!childLevel.getOrdinalExp().equals(
110                        childLevel.getKeyExp()))
111                {
112                    ++column;
113                }
114                column += childLevel.getProperties().length;
115            }
116            setCurrMember(member);
117        }
118        getList().add(member);
119        return column;
120    }
121
122    public List<Member> close() {
123        final boolean asList = this.constraint.getEvaluator() != null
124            && this.constraint.getEvaluator().getQuery().getResultStyle()
125            == ResultStyle.LIST;
126        final int limit = MondrianProperties.instance().ResultLimit.get();
127
128        final List<RolapMember> l = new AbstractList<RolapMember>() {
129            private boolean moreRows = true;
130            private int offset = 0;
131            private RolapMember first = null;
132            private boolean firstMemberAssigned = false;
133
134            /**
135             * Performs a load of the whole result set.
136             */
137            public int size() {
138                while (this.moreRows) {
139                    this.moreRows = sqlTupleReader.readNextTuple();
140                    if (limit > 0 && !asList && getList().size() > limit) {
141                        System.out.println(
142                            "Target: 199, Ouch! Toooo big array..."
143                            + hashCode());
144                        new Throwable().printStackTrace();
145                    }
146                }
147
148                return getList().size();
149            }
150
151            public RolapMember get(final int idx) {
152                if (asList) {
153                    return getList().get(idx);
154                }
155
156                if (idx == 0 && this.firstMemberAssigned) {
157                    return this.first;
158                }
159                int index = idx - offset;
160
161                if (0 < limit && index < 0) {
162                    // Cannot send NoSuchElementException since its intercepted
163                    // by AbstractSequentialList to identify out of bounds.
164                    throw new RuntimeException(
165                        "Element " + idx
166                        + " has been forgotten");
167                }
168
169                while (index >= getList().size() && this.moreRows) {
170                    this.moreRows = sqlTupleReader.readNextTuple();
171                    if (limit > 0 && getList().size() >= limit) {
172                        // We have to offset the list, but we don't want to use
173                        // a 0(n) operation. What we do is we calculate an
174                        // offset value corresponding to 10% of the current
175                        // size of the list and use subList(), which will
176                        // create a view of the sub array with an offset.
177                        //
178                        // We use a ~10% offset increment because we don't want
179                        // to perform this operation too often and impair
180                        // performance by spawning too many sublists.
181                        //
182                        // REVIEW: It doesn't matter right now if this code
183                        // is sub-optimal. The highCardinality feature is
184                        // marked for deprecation in 4.0. In practice,
185                        // this code should never get executed because if
186                        // you create a tuple list bigger than the value
187                        // of 'limit', the code will throw an exception
188                        // upstream before it ever reaches this point.
189                        // For now it is sufficient. We get the speed of
190                        // random access along with quick offsets.
191                        int deltaOffset = Math.round((limit / 10) + 0.5f);
192                        index -= deltaOffset;
193                        offset += deltaOffset;
194                        setList(
195                            getList().subList(
196                                deltaOffset,
197                                getList().size()));
198                    }
199                }
200
201                if (idx == 0) {
202                    this.first = getList().get(index);
203
204                    // Above might run into exception which is caught in
205                    // isEmpty(). So can change the state of the object after
206                    // that.
207                    this.firstMemberAssigned = true;
208                    return this.first;
209                } else {
210                    return getList().get(index);
211                }
212            }
213
214            public RolapMember set(final int i, final RolapMember e) {
215                if (asList) {
216                    return getList().set(i, e);
217                } else {
218                    throw new UnsupportedOperationException();
219                }
220            }
221
222            public boolean isEmpty() {
223                try {
224                    get(0);
225                    return false;
226                } catch (IndexOutOfBoundsException e) {
227                    return true;
228                }
229            }
230
231            public int hashCode() {
232                return Target.this.hashCode();
233            }
234
235            public Iterator<RolapMember> iterator() {
236                return new Iterator<RolapMember>() {
237                    private int cursor = 0;
238
239                    public boolean hasNext() {
240                        try {
241                            get(cursor);
242                            return true;
243                        } catch (IndexOutOfBoundsException ioobe) {
244                            return false;
245                        }
246                    }
247
248                    public RolapMember next() {
249                        return get(cursor++);
250                    }
251
252                    public void remove() {
253                        throw new UnsupportedOperationException();
254                    }
255                };
256            }
257        };
258
259        if (asList) {
260            l.size();
261        }
262
263        return Util.cast(l);
264    }
265}
266
267// End Target.java