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