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) 2011-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.calc; 011 012import mondrian.calc.impl.*; 013import mondrian.olap.*; 014 015import java.util.*; 016 017/** 018* Utility methods for tuple collections and iterators. 019 * 020 * @see TupleList 021 * @see TupleIterator 022 * 023 * @author jhyde 024 */ 025public final class TupleCollections { 026 private static final TupleList[] EMPTY_LISTS = { 027 new DelegatingTupleList(0, Collections.<List<Member>>emptyList()), 028 new UnaryTupleList(Collections.<Member>emptyList()), 029 new DelegatingTupleList(2, Collections.<List<Member>>emptyList()), 030 new DelegatingTupleList(3, Collections.<List<Member>>emptyList()), 031 new DelegatingTupleList(4, Collections.<List<Member>>emptyList()) 032 }; 033 034 // prevent instantiation 035 private TupleCollections() { 036 } 037 038 /** 039 * Creates a list of given arity. 040 * 041 * <p>If arity == 1, creates a {@link UnaryTupleList}; 042 * if arity == 0, creates a {@link DelegatingTupleList}; 043 * otherwise creates a {@link ArrayTupleList}. 044 * 045 * @see TupleList#cloneList(int) 046 * @see #createList(int, int) 047 * 048 * @param arity Arity 049 * @return Tuple list 050 */ 051 public static TupleList createList(int arity) { 052 switch (arity) { 053 case 0: 054 return new DelegatingTupleList(0, new ArrayList<List<Member>>()); 055 case 1: 056 return new UnaryTupleList(); 057 default: 058 return new ArrayTupleList(arity); 059 } 060 } 061 062 /** 063 * Creates a list of given arity and initial capacity. 064 * 065 * <p>If arity == 1, creates a {@link UnaryTupleList}; 066 * if arity == 0, creates a {@link DelegatingTupleList}; 067 * otherwise creates a {@link ArrayTupleList}. 068 * 069 * @see TupleList#cloneList(int) 070 * 071 * @param arity Arity 072 * @param initialCapacity Initial capacity 073 * @return Tuple list 074 */ 075 public static TupleList createList(int arity, int initialCapacity) { 076 switch (arity) { 077 case 0: 078 return new DelegatingTupleList( 079 0, new ArrayList<List<Member>>(initialCapacity)); 080 case 1: 081 return new UnaryTupleList(new ArrayList<Member>(initialCapacity)); 082 default: 083 return new ArrayTupleList(arity, initialCapacity); 084 } 085 } 086 087 /** 088 * Returns an empty TupleList of given arity. 089 * 090 * @param arity Number of members per tuple 091 * @return Empty tuple list 092 */ 093 public static TupleList emptyList(int arity) { 094 return arity < EMPTY_LISTS.length 095 ? EMPTY_LISTS[arity] 096 : new DelegatingTupleList( 097 arity, Collections.<List<Member>>emptyList()); 098 } 099 100 /** 101 * Creates an unmodifiable TupleList backed by a given list. 102 * 103 * @see Collections#unmodifiableList(java.util.List) 104 * 105 * @param list the list for which an unmodifiable view is to be returned. 106 * @return an unmodifiable view of the specified list. 107 */ 108 public static TupleList unmodifiableList(TupleList list) { 109 return list.getArity() == 1 110 ? new UnaryTupleList( 111 Collections.unmodifiableList( 112 list.slice(0))) 113 : new DelegatingTupleList( 114 list.getArity(), 115 Collections.unmodifiableList( 116 list)); 117 } 118 119 /** 120 * Adapts a {@link TupleCursor} into a {@link TupleIterator}. 121 * 122 * <p>Since the latter is a more difficult API to implement, the wrapper 123 * has some extra state. 124 * 125 * <p>This method may be used to implement 126 * {@link mondrian.calc.TupleIterable#tupleIterator()} for a 127 * {@link TupleIterable} or {@link TupleList} that only has a 128 * {@code TupleCursor} implementation. 129 * 130 * @param cursor Cursor 131 * @return Tuple iterator view onto the cursor 132 */ 133 public static TupleIterator iterator(final TupleCursor cursor) { 134 if (cursor instanceof TupleIterator) { 135 return (TupleIterator) cursor; 136 } 137 return new AbstractTupleIterator(cursor.getArity()) { 138 private int state = STATE_UNKNOWN; 139 140 private static final int STATE_UNKNOWN = 0; 141 private static final int STATE_HASNEXT = 1; 142 private static final int STATE_EOD = 2; 143 144 public List<Member> current() { 145 return cursor.current(); 146 } 147 148 @Override 149 public boolean hasNext() { 150 switch (state) { 151 case STATE_UNKNOWN: 152 if (cursor.forward()) { 153 state = STATE_HASNEXT; 154 return true; 155 } else { 156 state = STATE_EOD; 157 return false; 158 } 159 case STATE_EOD: 160 return false; 161 case STATE_HASNEXT: 162 return true; 163 default: 164 throw new RuntimeException("unpexected state " + state); 165 } 166 } 167 168 @Override 169 public List<Member> next() { 170 switch (state) { 171 case STATE_UNKNOWN: 172 if (cursor.forward()) { 173 return cursor.current(); 174 } 175 state = STATE_EOD; 176 // fall through 177 case STATE_EOD: 178 throw new NoSuchElementException(); 179 case STATE_HASNEXT: 180 state = STATE_UNKNOWN; 181 return cursor.current(); 182 default: 183 throw new RuntimeException("unpexected state " + state); 184 } 185 } 186 187 public boolean forward() { 188 switch (state) { 189 case STATE_UNKNOWN: 190 return cursor.forward(); 191 case STATE_HASNEXT: 192 state = STATE_UNKNOWN; 193 return true; 194 case STATE_EOD: 195 return false; 196 default: 197 throw new RuntimeException("unpexected state " + state); 198 } 199 } 200 201 @Override 202 public void setContext(Evaluator evaluator) { 203 cursor.setContext(evaluator); 204 } 205 206 @Override 207 public void currentToArray(Member[] members, int offset) { 208 cursor.currentToArray(members, offset); 209 } 210 211 @Override 212 public Member member(int column) { 213 return cursor.member(column); 214 } 215 }; 216 } 217 218 /** 219 * Creates a slice of a {@link TupleIterable}. 220 * 221 * <p>Can be used as an implementation for 222 * {@link mondrian.calc.TupleList#slice(int)}. 223 * 224 * @param tupleIterable Iterable 225 * @param column Which member of each tuple of project. 226 * @return Iterable that returns a given member of each tuple 227 */ 228 public static Iterable<Member> slice( 229 final TupleIterable tupleIterable, 230 final int column) 231 { 232 if (column < 0 || column >= tupleIterable.getArity()) { 233 throw new IllegalArgumentException(); 234 } 235 return new Iterable<Member>() { 236 public Iterator<Member> iterator() { 237 final TupleIterator tupleIterator = 238 tupleIterable.tupleIterator(); 239 return new Iterator<Member>() { 240 public boolean hasNext() { 241 return tupleIterator.hasNext(); 242 } 243 244 public Member next() { 245 if (!tupleIterator.forward()) { 246 throw new NoSuchElementException(); 247 } 248 return tupleIterator.member(column); 249 } 250 251 public void remove() { 252 throw new UnsupportedOperationException(); 253 } 254 }; 255 } 256 }; 257 } 258 259 /** 260 * Converts a {@link mondrian.calc.TupleIterable} to an old-style iterable that 261 * creates an iterator over member arrays. 262 * 263 * @param tupleIterable Tuple iterable 264 * @return Iterable that creates an iterator over tuples represented as 265 * member arrays 266 */ 267 public static Iterable<Member[]> asMemberArrayIterable( 268 final TupleIterable tupleIterable) 269 { 270 return new Iterable<Member[]>() { 271 public Iterator<Member[]> iterator() { 272 return new Iterator<Member[]>() { 273 final TupleIterator 274 tupleIterator = tupleIterable.tupleIterator(); 275 public boolean hasNext() { 276 return tupleIterator.hasNext(); 277 } 278 279 public Member[] next() { 280 final List<Member> next = tupleIterator.next(); 281 return next.toArray(new Member[next.size()]); 282 } 283 284 public void remove() { 285 throw new UnsupportedOperationException(); 286 } 287 }; 288 } 289 }; 290 } 291 292 /** 293 * Converts a {@link mondrian.calc.TupleList} to an old-style list of member 294 * arrays. 295 * 296 * @param tupleList Tuple list 297 * @return List of member arrays 298 */ 299 public static List<Member[]> asMemberArrayList( 300 final TupleList tupleList) 301 { 302 return new AbstractList<Member[]>() { 303 @Override 304 public Member[] get(int index) { 305 final List<Member> tuple = tupleList.get(index); 306 return tuple.toArray(new Member[tuple.size()]); 307 } 308 309 @Override 310 public int size() { 311 return tupleList.size(); 312 } 313 }; 314 } 315 316 /** 317 * Converts an old-style list (members or member arrays) to a 318 * {@link mondrian.calc.TupleList}. 319 * 320 * <p>Deduces the arity of the list from the first element, if the list 321 * is not empty. Otherwise assumes arity 1. 322 * 323 * <p>If the list happens to be a tuple list, returns unchanged. 324 * 325 * @param list Old-style list 326 * @return Tuple list 327 */ 328 public static TupleList asTupleList(List list) { 329 if (list instanceof TupleList) { 330 return (TupleList) list; 331 } 332 if (list.isEmpty()) { 333 return TupleCollections.emptyList(1); 334 } 335 final Object o = list.get(0); 336 if (o instanceof Member) { 337 final List<Member> memberList = Util.cast(list); 338 return new UnaryTupleList(memberList); 339 } else { 340 final List<Member[]> memberArrayList = Util.cast(list); 341 return new DelegatingTupleList( 342 memberArrayList.get(0).length, 343 new AbstractList<List<Member>>() { 344 public List<Member> get(int index) { 345 return Arrays.asList(memberArrayList.get(index)); 346 } 347 348 public int size() { 349 return memberArrayList.size(); 350 } 351 } 352 ); 353 } 354 } 355 356 /** 357 * Converts a {@link TupleIterable} into a {@link TupleList}. 358 * 359 * <p>If the iterable is already a list, returns the iterable. If it is not 360 * a list, the behavior depends on the {@code eager} parameter. With eager = 361 * true, creates a list and populates it with the contents of the 362 * iterable. With eager = false, wraps in an adapter that implements the 363 * list interface and materializes to a list the first time that an 364 * operation that is in TupleList but not TupleIterable -- for example, 365 * {@link TupleList#get} or {@link TupleList#size} -- is called. 366 * 367 * @param tupleIterable Iterable 368 * @param eager Whether to convert into a list now, as opposed to on first 369 * use of a random-access method such as size or get. 370 * @return List 371 */ 372 public static TupleList materialize( 373 TupleIterable tupleIterable, 374 boolean eager) 375 { 376 if (tupleIterable instanceof TupleList) { 377 return (TupleList) tupleIterable; 378 } 379 if (eager) { 380 TupleList tupleList = createList(tupleIterable.getArity()); 381 TupleCursor tupleCursor = tupleIterable.tupleCursor(); 382 while (tupleCursor.forward()) { 383 tupleList.addCurrent(tupleCursor); 384 } 385 return tupleList; 386 } else { 387 return new MaterializingTupleList(tupleIterable); 388 } 389 } 390 391 /** 392 * Implementation of {@link TupleList} that is based on a 393 * {@link TupleIterable} and materializes into a read-only list the first 394 * time that an method is called that requires a list. 395 */ 396 private static class MaterializingTupleList 397 implements TupleList 398 { 399 private final TupleIterable tupleIterable; 400 TupleList tupleList; 401 402 public MaterializingTupleList( 403 TupleIterable tupleIterable) 404 { 405 this.tupleIterable = tupleIterable; 406 } 407 408 private TupleList materialize() { 409 if (tupleList == null) { 410 tupleList = TupleCollections.materialize(tupleIterable, true); 411 } 412 return tupleList; 413 } 414 415 // TupleIterable methods 416 417 public TupleIterator tupleIterator() { 418 if (tupleList == null) { 419 return tupleIterable.tupleIterator(); 420 } else { 421 return tupleList.tupleIterator(); 422 } 423 } 424 425 public TupleCursor tupleCursor() { 426 if (tupleList == null) { 427 return tupleIterable.tupleCursor(); 428 } else { 429 return tupleList.tupleCursor(); 430 } 431 } 432 433 public int getArity() { 434 return tupleIterable.getArity(); 435 } 436 437 public Iterator<List<Member>> iterator() { 438 if (tupleList == null) { 439 return tupleIterable.iterator(); 440 } else { 441 return tupleList.iterator(); 442 } 443 } 444 445 public List<Member> slice(int column) { 446 // Note that TupleIterable has 'Iterable<Member> slice(int)' 447 // and TupleList has 'List<Member> slice(int)'. 448 // So, if this list is not materialized, we could return a slice of 449 // the un-materialized iterable. But it's not worth the complexity. 450 return materialize().slice(column); 451 } 452 453 public Member get(int slice, int index) { 454 return materialize().get(slice, index); 455 } 456 457 public TupleList cloneList(int capacity) { 458 return materialize().cloneList(capacity); 459 } 460 461 public void addTuple(Member... members) { 462 throw new UnsupportedOperationException(); 463 } 464 465 public TupleList project(int[] destIndices) { 466 return materialize().project(destIndices); 467 } 468 469 public void addCurrent(TupleCursor tupleIter) { 470 materialize().addCurrent(tupleIter); 471 } 472 473 public TupleList subList(int fromIndex, int toIndex) { 474 return materialize().subList(fromIndex, toIndex); 475 } 476 477 public TupleList withPositionCallback( 478 PositionCallback positionCallback) 479 { 480 return materialize().withPositionCallback(positionCallback); 481 } 482 483 public TupleList fix() { 484 return materialize().fix(); 485 } 486 487 public int size() { 488 return materialize().size(); 489 } 490 491 public boolean isEmpty() { 492 return materialize().isEmpty(); 493 } 494 495 public boolean contains(Object o) { 496 return materialize().contains(o); 497 } 498 499 public Object[] toArray() { 500 return materialize().toArray(); 501 } 502 503 public <T> T[] toArray(T[] a) { 504 return materialize().toArray(a); 505 } 506 507 public boolean add(List<Member> members) { 508 throw new UnsupportedOperationException(); 509 } 510 511 public boolean remove(Object o) { 512 throw new UnsupportedOperationException(); 513 } 514 515 public boolean containsAll(Collection<?> c) { 516 return materialize().containsAll(c); 517 } 518 519 public boolean addAll(Collection<? extends List<Member>> c) { 520 throw new UnsupportedOperationException(); 521 } 522 523 public boolean addAll(int index, Collection<? extends List<Member>> c) { 524 throw new UnsupportedOperationException(); 525 } 526 527 public boolean removeAll(Collection<?> c) { 528 throw new UnsupportedOperationException(); 529 } 530 531 public boolean retainAll(Collection<?> c) { 532 throw new UnsupportedOperationException(); 533 } 534 535 public void clear() { 536 throw new UnsupportedOperationException(); 537 } 538 539 public List<Member> get(int index) { 540 return materialize().get(index); 541 } 542 543 public List<Member> set(int index, List<Member> element) { 544 throw new UnsupportedOperationException(); 545 } 546 547 public void add(int index, List<Member> element) { 548 throw new UnsupportedOperationException(); 549 } 550 551 public List<Member> remove(int index) { 552 throw new UnsupportedOperationException(); 553 } 554 555 public int indexOf(Object o) { 556 return materialize().indexOf(o); 557 } 558 559 public int lastIndexOf(Object o) { 560 return materialize().lastIndexOf(o); 561 } 562 563 public ListIterator<List<Member>> listIterator() { 564 return materialize().listIterator(); 565 } 566 567 public ListIterator<List<Member>> listIterator(int index) { 568 return materialize().listIterator(index); 569 } 570 } 571} 572 573// End TupleCollections.java