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