001    /*
002    // This software is subject to the terms of the Common Public License
003    // Agreement, available at the following URL:
004    // http://www.opensource.org/licenses/cpl.html.
005    // Copyright (C) 2004-2005 TONBELLER AG
006    // Copyright (C) 2005-2008 Julian Hyde and others
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    
011    /*
012    // This software is subject to the terms of the Common Public License
013    // Agreement, available at the following URL:
014    // http://www.opensource.org/licenses/cpl.html.
015    // Copyright (C) 2004-2005 TONBELLER AG
016    // Copyright (C) 2005-2008 Julian Hyde and others
017    // All Rights Reserved.
018    // You must accept the terms of that agreement to use this software.
019    */
020    package mondrian.rolap;
021    
022    import mondrian.calc.ResultStyle;
023    import mondrian.olap.*;
024    import mondrian.olap.fun.FunUtil;
025    import mondrian.resource.MondrianResource;
026    import mondrian.rolap.sql.MemberChildrenConstraint;
027    import mondrian.rolap.sql.SqlQuery;
028    import mondrian.rolap.sql.TupleConstraint;
029    import mondrian.util.*;
030    
031    import javax.sql.DataSource;
032    import java.sql.ResultSet;
033    import java.sql.SQLException;
034    import java.util.*;
035    
036    /**
037     * Extracted Target from original place.
038     * Unknown functonallity.
039     */
040    public class Target {
041        private final HighCardSqlTupleReader sqlTupleReader;
042        private final RolapLevel level;
043        private final MemberCache cache;
044        private final TupleConstraint constraint;
045        private final TupleReader.MemberBuilder memberBuilder;
046        private final List<RolapMember> srcMembers;
047    
048        boolean parentChild;
049        private RolapLevel[] levels;
050        private RolapMember currMember;
051        private List<List<RolapMember>> siblings;
052        private List<RolapMember> members;
053        private int levelDepth;
054        private List<RolapMember> list;
055    
056        public Target(final RolapLevel level,
057                final TupleReader.MemberBuilder memberBuilder,
058                final List<RolapMember> srcMembers,
059                final TupleConstraint constraint,
060                final HighCardSqlTupleReader sqlTupleReader) {
061            this.sqlTupleReader = sqlTupleReader;
062            this.level = level;
063            this.constraint = constraint;
064            this.cache = memberBuilder.getMemberCache();
065            this.memberBuilder = memberBuilder;
066            this.srcMembers = srcMembers;
067        }
068    
069        public List<RolapMember> getSrcMembers() {
070            return this.srcMembers;
071        }
072    
073        public RolapMember getCurrMember() {
074            return this.currMember;
075        }
076    
077        public void removeCurrMember() {
078            this.currMember = null;
079        }
080    
081        public void setCurrMember(final RolapMember m) {
082            this.currMember = m;
083        }
084    
085        public void add(final RolapMember member) {
086            this.list.add(member);
087        }
088    
089        public void open() {
090            levels = (RolapLevel[]) level.getHierarchy().getLevels();
091            list = new LinkedList<RolapMember>();
092            levelDepth = level.getDepth();
093            parentChild = level.isParentChild();
094            // members[i] is the current member of level#i, and siblings[i]
095            // is the current member of level#i plus its siblings
096            members = new ArrayList<RolapMember>();
097            for (int i = 0; i < levels.length; i++) {
098                members.add(null);
099            }
100            siblings = new ArrayList<List<RolapMember>>();
101            for (int i = 0; i < levels.length + 1; i++) {
102                siblings.add(new ArrayList<RolapMember>());
103            }
104        }
105    
106        /**
107         * Scans a row of the resultset and creates a member
108         * for the result.
109         *
110         * @param resultSet result set to retrieve rows from
111         * @param column the column index to start with
112         *
113         * @return index of the last column read + 1
114         * @throws SQLException
115         */
116        public int addRow(ResultSet resultSet, int column) throws SQLException {
117            synchronized (cache) {
118                return internalAddRow(resultSet, column);
119            }
120        }
121    
122        private int internalAddRow(ResultSet resultSet, int column)
123                throws SQLException {
124            RolapMember member = null;
125            if (currMember != null) {
126                member = currMember;
127            } else {
128                boolean checkCacheStatus = true;
129                for (int i = 0; i <= levelDepth; i++) {
130                    RolapLevel childLevel = levels[i];
131                    if (childLevel.isAll()) {
132                        member = level.getHierarchy().getAllMember();
133                        continue;
134                    }
135                    Object value = resultSet.getObject(++column);
136                    if (value == null) {
137                        value = RolapUtil.sqlNullValue;
138                    }
139                    Object captionValue;
140                    if (childLevel.hasCaptionColumn()) {
141                        captionValue = resultSet.getObject(++column);
142                    } else {
143                        captionValue = null;
144                    }
145                    RolapMember parentMember = member;
146                    Object key = cache.makeKey(parentMember, value);
147                    member = cache.getMember(key, checkCacheStatus);
148                    checkCacheStatus = false; /* Only check the first time */
149                    if (member == null) {
150                        member = memberBuilder.makeMember(
151                            parentMember, childLevel, value, captionValue,
152                            parentChild, resultSet, key, column);
153                    }
154    
155                    // Skip over the columns consumed by makeMember
156                    if (!childLevel.getOrdinalExp().equals(
157                        childLevel.getKeyExp()))
158                    {
159                        ++column;
160                    }
161                    column += childLevel.getProperties().length;
162                }
163                currMember = member;
164            }
165            ((List) list).add(member);
166            return column;
167        }
168    
169        public List<RolapMember> close() {
170            final boolean asList = this.constraint.getEvaluator() != null
171                    && this.constraint.getEvaluator().getQuery().getResultStyle()
172                        == ResultStyle.LIST;
173            final int limit = MondrianProperties.instance().ResultLimit.get();
174    
175            final List<RolapMember> l = new AbstractList<RolapMember>() {
176                private boolean moreRows = true;
177                private int offset = 0;
178                private RolapMember first = null;
179                private boolean firstMemberAssigned = false;
180    
181                /**
182                 * Performs a load of the whole result set.
183                 */
184                public int size() {
185                    while (this.moreRows) {
186                        this.moreRows = sqlTupleReader.readNextTuple();
187                        if (limit > 0 && !asList && list.size() > limit) {
188                            System.out.println("Target: 199, Ouch! Toooo big array..." + this.hashCode());
189                            new Throwable().printStackTrace();
190                        }
191                    }
192    
193                    return list.size();
194                }
195    
196                public RolapMember get(final int idx) {
197                    if (asList) {
198                        return list.get(idx);
199                    }
200    
201                    if (idx == 0 && this.firstMemberAssigned) {
202                        return this.first;
203                    }
204                    int index = idx - offset;
205    
206                    if (0 < limit && index < 0) {
207                        // Cannot send NoSuchElementException since its intercepted
208                        // by AbstractSequentialList to identify out of bounds.
209                        throw new RuntimeException("Element " + idx
210                                + " has been forgotten");
211                    }
212    
213                    while (index >= list.size() && this.moreRows) {
214                        this.moreRows = sqlTupleReader.readNextTuple();
215                        if (!asList && limit > 0 && list.size() > limit) {
216                            while (list.size() > limit) {
217                                index--;
218                                offset++;
219                                ((LinkedList) list).removeFirst();
220                            }
221                        }
222                    }
223    
224                    if (idx == 0) {
225                        this.firstMemberAssigned = true;
226                        this.first = list.get(index);
227                        return this.first;
228                    } else {
229                        return list.get(index);
230                    }
231                }
232    
233                public RolapMember set(final int i, final RolapMember e) {
234                    if (asList) {
235                        return list.set(i, e);
236                    } else {
237                        throw new UnsupportedOperationException();
238                    }
239                }
240    
241                public boolean isEmpty() {
242                    try {
243                        get(0);
244                        return false;
245                    } catch (IndexOutOfBoundsException e) {
246                        return true;
247                    }
248                }
249    
250                public int hashCode() {
251                    return Target.this.hashCode();
252                }
253    
254                public Iterator<RolapMember> iterator() {
255                    return new Iterator<RolapMember>() {
256                        private int cursor = 0;
257    
258                        public boolean hasNext() {
259                            try {
260                                get(cursor);
261                                return true;
262                            } catch (IndexOutOfBoundsException ioobe) {
263                                return false;
264                            }
265                        }
266    
267                        public RolapMember next() {
268                            return get(cursor++);
269                        }
270    
271                        public void remove() {
272                            throw new UnsupportedOperationException();
273                        }
274                    };
275                }
276            };
277    
278            if (asList) {
279                l.size();
280            }
281    
282            return l;
283    
284    /*
285            synchronized (cache) {
286                return internalClose();
287            }
288    */
289        }
290    
291    
292        //
293        // Private Stuff --------------------------------------------------
294        //
295    
296        /**
297         * Cleans up after all rows have been processed, and returns the list of
298         * members.
299         *
300         * @return list of members
301         */
302        private List<RolapMember> internalClose() {
303            for (int i = 0; i < members.size(); i++) {
304                RolapMember member = members.get(i);
305                final List<RolapMember> children = siblings.get(i + 1);
306                if (member != null && children != null) {
307                    // If we are finding the members of a particular level, and
308                    // we happen to find some of the children of an ancestor of
309                    // that level, we can't be sure that we have found all of
310                    // the children, so don't put them in the cache.
311                    if (member.getDepth() < level.getDepth()) {
312                        continue;
313                    }
314                    MemberChildrenConstraint mcc =
315                        constraint.getMemberChildrenConstraint(member);
316                    if (mcc != null) {
317                        cache.putChildren(member, mcc, children);
318                    }
319                }
320            }
321            return list;
322        }
323    
324        /**
325         * Adds <code>member</code> just before the first element in
326         * <code>list</code> which has the same parent.
327         */
328        private void addAsOldestSibling(final List<RolapMember> list,
329                final RolapMember member) {
330            int i = list.size();
331            while (--i >= 0) {
332                RolapMember sibling = list.get(i);
333                if (sibling.getParentMember() != member.getParentMember()) {
334                    break;
335                }
336            }
337            list.add(i + 1, member);
338        }
339    
340        public RolapLevel getLevel() {
341            return level;
342        }
343    
344        public String toString() {
345            return level.getUniqueName();
346        }
347    }
348    
349    // End Target.java