001 /*
002 // $Id: //open/mondrian-release/3.2/src/main/mondrian/rolap/RolapAxis.java#1 $
003 // This software is subject to the terms of the Eclipse Public License v1.0
004 // Agreement, available at the following URL:
005 // http://www.eclipse.org/legal/epl-v10.html.
006 // Copyright (C) 2005-2009 Julian Hyde
007 // All Rights Reserved.
008 // You must accept the terms of that agreement to use this software.
009 */
010
011 package mondrian.rolap;
012
013
014 import mondrian.olap.Axis;
015 import mondrian.olap.Member;
016 import mondrian.olap.Position;
017 import mondrian.util.UnsupportedList;
018 import org.apache.log4j.Logger;
019
020 import java.util.Collections;
021 import java.util.ListIterator;
022 import java.util.Iterator;
023 import java.util.ArrayList;
024 import java.util.List;
025
026 /**
027 * Derived classes of RolapAxis implements the Axis interface which are
028 * specializations based upon the number of Positions, how each Position's
029 * Members are orgainized and whether the Members/Member[]s are in a List
030 * or an Iterable.
031 *
032 * @author <a>Richard M. Emberson</a>
033 * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/rolap/RolapAxis.java#1 $
034 */
035 public abstract class RolapAxis implements Axis {
036 private static final Logger LOGGER = Logger.getLogger(RolapAxis.class);
037
038 public static String toString(Axis axis) {
039 List<Position> pl = axis.getPositions();
040 return toString(pl);
041 }
042
043 public static String toString(List<Position> pl) {
044 StringBuilder buf = new StringBuilder();
045 for (Position p : pl) {
046 buf.append('{');
047 boolean firstTime = true;
048 for (Member m : p) {
049 if (! firstTime) {
050 buf.append(", ");
051 }
052 buf.append(m.getUniqueName());
053 firstTime = false;
054 }
055 buf.append('}');
056 buf.append('\n');
057 }
058 return buf.toString();
059 }
060
061 /**
062 * A Wrapper has many uses. In particular, if one is using Java 5 or
063 * above, one can create a Wrapper that is also a memory usage listener.
064 * Then one can place an Axis implementation into a Wrapper where the
065 * initial implementation is in-memory, large-memory-usage and
066 * cpu fast. The on the first memory notification it can be migrated
067 * to an in-memory, small-memory-usage and cpu slower. On a subsequent
068 * memory notification it can be migrated to an on-disk, low-memory and
069 * cpu slow implementation.
070 */
071 public static class Wrapper extends RolapAxis {
072 private final Axis axis;
073 protected Wrapper(Axis axis) {
074 super();
075 this.axis = axis;
076 }
077 public List<Position> getPositions() {
078 return this.axis.getPositions();
079 }
080 }
081
082 /**
083 * The NoPosition Axis implementation is an Axis that has no Positions,
084 * the size of the list of positions is zero.
085 */
086 public static class NoPosition extends RolapAxis {
087 public NoPosition() {
088 super();
089 }
090 public List<Position> getPositions() {
091 return Collections.EMPTY_LIST;
092 }
093 }
094
095 /**
096 * The PositionList Axis implementation takes a List of positions.
097 */
098 public static class PositionList extends RolapAxis {
099 protected final List<Position> positions;
100 public PositionList(List<Position> positions) {
101 super();
102 this.positions = positions;
103 }
104 public List<Position> getPositions() {
105 return positions;
106 }
107 }
108
109 /**
110 * A SingleEmptyPosition has a single Position and the Position has
111 * no Members.
112 */
113 public static class SingleEmptyPosition extends RolapAxis {
114 public SingleEmptyPosition() {
115 }
116 public List<Position> getPositions() {
117 return Collections.singletonList((Position) new EmptyPosition());
118 }
119 static class EmptyPosition extends PositionBase {
120 EmptyPosition() {
121 }
122 public int size() {
123 return 0;
124 }
125 public Member get(int index) {
126 throw new IndexOutOfBoundsException(
127 "Index: " + index + ", Size: 0");
128 }
129 }
130 }
131
132 /**
133 * A MemberIterable takes an Iterable<Member> where each Position has
134 * a single Member from the corresponding location in the iterator.
135 * If the client request any of the List, non-Iterable, API, then
136 * a List is materialized from the Iterable.
137 */
138 public static class MemberIterable extends RolapAxis {
139 private Iterable<Member> iter;
140 private List<Member> list;
141 public MemberIterable(Iterable<Member> iter) {
142 this.iter = iter;
143 this.list = null;
144 }
145 public synchronized List<Position> getPositions() {
146 return (list == null)
147 ? new MemberIterable.PositionWrapper()
148 : new MemberIterable.PositionList();
149 }
150 protected synchronized void materialize() {
151 if (list == null) {
152 Iterator<Member> it = iter.iterator();
153 list = new ArrayList<Member>();
154 while (it.hasNext()) {
155 list.add(it.next());
156 }
157 // allow gc of iter
158 iter = null;
159 }
160 }
161
162
163 /**
164 * This List<Position> starts life with a List<Position>
165 * implementation
166 * that is based upon an non-List (Iterable). If all accesses
167 * are simply through iteration, then the initial implementation
168 * remains, but if the client uses either the 'size' or 'get' methods
169 * then the Iterable is materialized into a List.
170 */
171 class PositionWrapper extends PositionListUnsupported {
172 List<Position> positionList;
173
174 PositionWrapper() {
175 positionList = new PositionIter();
176 }
177
178 protected synchronized void materialize() {
179 if (LOGGER.isDebugEnabled()) {
180 LOGGER.debug(
181 "PositionWrapper.materialize: Member iter.class="
182 + iter.getClass().getName());
183 }
184 RolapAxis.MemberIterable.this.materialize();
185 positionList = new MemberIterable.PositionList();
186 }
187
188 public int size() {
189 try {
190 return positionList.size();
191 } catch (UnsupportedOperationException ex) {
192 this.materialize();
193 return positionList.size();
194 }
195 }
196
197 public Position get(int index) {
198 try {
199 return positionList.get(index);
200 } catch (UnsupportedOperationException ex) {
201 this.materialize();
202 return positionList.get(index);
203 }
204 }
205
206 public Iterator<Position> iterator() {
207 return positionList.iterator();
208 }
209 }
210
211 /**
212 * PositionIter is a List<Position> that only support the
213 * 'iterator' method. This assumes that one iterates over Positions
214 * and for each Postion one iterates over Members. In this case,
215 * each Position has a single Member.
216 */
217 class PositionIter extends PositionIterBase {
218 private Iterator<Member> it;
219
220 PositionIter() {
221 it = iter.iterator();
222 }
223
224 public Iterator<Position> iterator() {
225 return new Iterator<Position>() {
226 public boolean hasNext() {
227 return it.hasNext();
228 }
229
230 public Position next() {
231 return new MemberIterPosition(it.next());
232 }
233
234 public void remove() {
235 throw new UnsupportedOperationException("remove");
236 }
237 };
238 }
239 }
240
241 /**
242 * A List<Member> which only implements the 'iterator' method.
243 * Each Iterator<Member> has only one Member.
244 */
245 class MemberIterPosition extends PositionBase {
246 Member member;
247
248 MemberIterPosition(Member member) {
249 this.member = member;
250 }
251
252 public int size() {
253 return 1;
254 }
255
256 public Member get(int index) {
257 if (index != 0) {
258 throw new IndexOutOfBoundsException(
259 "Index: " + index + ", Size: 1");
260 }
261 return member;
262 }
263
264 public Iterator<Member> iterator() {
265 return new Iterator<Member>() {
266 public boolean hasNext() {
267 return (member != null);
268 }
269
270 public Member next() {
271 try {
272 return member;
273 } finally {
274 member = null;
275 }
276 }
277
278 public void remove() {
279 throw new UnsupportedOperationException("remove");
280 }
281 };
282 }
283 }
284
285 /**
286 * Each Position has a single Member.
287 */
288 class PositionList extends PositionListBase {
289 PositionList() {
290 }
291
292 public boolean isEmpty() {
293 // may be considerably cheaper than computing size
294 return list.isEmpty();
295 }
296
297 public int size() {
298 return list.size();
299 }
300
301 public Position get(int index) {
302 return new MemberListPosition(index);
303 }
304 }
305
306 /**
307 * Allows access only the the Member at the given offset.
308 */
309 class MemberListPosition extends PositionBase {
310 protected final int offset;
311
312 MemberListPosition(int offset) {
313 this.offset = offset;
314 }
315
316 public int size() {
317 return 1;
318 }
319
320 public Member get(int index) {
321 if (index != 0) {
322 throw new IndexOutOfBoundsException(
323 "Index: " + index + ", Size: 1");
324 }
325 return list.get(offset);
326 }
327 }
328 }
329
330 /**
331 * A MemberList takes a List<Member> where each Position has
332 * a single Member from the corresponding location in the list.
333 */
334 public static class MemberList extends RolapAxis {
335 private final List<Member> list;
336
337 public MemberList(List<Member> list) {
338 this.list = list;
339 }
340
341 public List<Position> getPositions() {
342 return new MemberList.PositionList();
343 }
344
345 /**
346 * Each Position has a single Member.
347 */
348 class PositionList extends PositionListBase {
349 PositionList() {
350 }
351
352 public int size() {
353 return list.size();
354 }
355
356 public boolean isEmpty() {
357 // may be considerably cheaper than computing size
358 return list.isEmpty();
359 }
360
361 public Position get(int index) {
362 return new MemberListPosition(index);
363 }
364
365 public Iterator<Position> iterator() {
366 return new Iterator<Position>() {
367 private final Iterator it = list.iterator();
368 private int cursor = 0;
369 public boolean hasNext() {
370 return it.hasNext();
371 }
372
373 public Position next() {
374 it.next();
375 return get(cursor++);
376 }
377
378 public void remove() {
379 throw new UnsupportedOperationException();
380 }
381 };
382 }
383 }
384
385 /**
386 * Allows access only the the Member at the given offset.
387 */
388 class MemberListPosition extends PositionBase {
389 protected final int offset;
390
391 MemberListPosition(int offset) {
392 this.offset = offset;
393 }
394
395 public int size() {
396 return 1;
397 }
398
399 public Member get(int index) {
400 if (index != 0) {
401 throw new IndexOutOfBoundsException(
402 "Index: " + index + ", Size: 1");
403 }
404 return list.get(offset);
405 }
406 }
407 }
408
409 /**
410 * A MemberArrayIterable takes an Iterable<Member[]> where
411 * each Position has
412 * an array of Members from the corresponding location in the iterator.
413 * If the client request any of the List, non-Iterable, API, then
414 * a List is materialized from the Iterable.
415 */
416 public static class MemberArrayIterable extends RolapAxis {
417 private Iterable<Member[]> iter;
418 private List<Member[]> list;
419 private int len;
420
421 public MemberArrayIterable(Iterable<Member[]> iter) {
422 this.iter = iter;
423 this.list = null;
424 this.len = 0;
425 }
426
427 public synchronized List<Position> getPositions() {
428 return (list == null)
429 ? new MemberArrayIterable.PositionWrapper()
430 : new MemberArrayIterable.PositionList();
431 }
432
433 protected synchronized void materialize() {
434 if (list == null) {
435 Iterator<Member[]> it = iter.iterator();
436 list = new ArrayList<Member[]>();
437 while (it.hasNext()) {
438 list.add(it.next());
439 }
440 // allow gc of iter
441 iter = null;
442
443 len = (list.size() == 0) ? 0 : list.get(0).length;
444 }
445 }
446
447 /**
448 * This List<Position> starts life with a List<Position>
449 * implementation
450 * that is based upon an non-List (Iterable). If all accesses
451 * are simply through iteration, then the initial implementation
452 * remains, but if the client uses either the 'size' or 'get' methods
453 * then the Iterable is materialized into a List.
454 */
455 class PositionWrapper extends PositionListUnsupported {
456 List<Position> positionList;
457 PositionWrapper() {
458 positionList = new PositionIter();
459 }
460
461 protected synchronized void materialize() {
462 if (LOGGER.isDebugEnabled()) {
463 LOGGER.debug(
464 "PositionWrapper.materialize: Member[] iter.class="
465 + ((iter != null) ? iter.getClass().getName() : null));
466 }
467 RolapAxis.MemberArrayIterable.this.materialize();
468 positionList = new MemberArrayIterable.PositionList();
469 }
470
471 public int size() {
472 try {
473 return positionList.size();
474 } catch (UnsupportedOperationException ex) {
475 this.materialize();
476 return positionList.size();
477 }
478 }
479
480 public Position get(int index) {
481 try {
482 return positionList.get(index);
483 } catch (UnsupportedOperationException ex) {
484 this.materialize();
485 return positionList.get(index);
486 }
487 }
488
489 public Iterator<Position> iterator() {
490 return positionList.iterator();
491 }
492 }
493
494 /**
495 * PositionIter is a List<Position> that only support the
496 * 'iterator' method. This assumes that one iterates over Positions
497 * and for each Postion one iterates over Members. Each Position
498 * has two or more Members.
499 */
500 class PositionIter extends PositionIterBase {
501 private Iterator<Member[]> it;
502
503 PositionIter() {
504 it = iter.iterator();
505 }
506
507 public Iterator<Position> iterator() {
508 return new Iterator<Position>() {
509 int nextCnt = 0;
510
511 public boolean hasNext() {
512 return it.hasNext();
513 }
514
515 public Position next() {
516 nextCnt++;
517 return new MemberIterPosition(it.next());
518 }
519
520 public void remove() {
521 throw new UnsupportedOperationException("remove");
522 }
523 };
524 }
525 }
526
527 /**
528 * A List<Member> which only implements the 'iterator' method.
529 * Each Iterator<Member> two or more Members.
530 */
531 class MemberIterPosition extends PositionBase {
532 Member[] members;
533
534 MemberIterPosition(Member[] members) {
535 this.members = members;
536 }
537
538 public int size() {
539 return members.length;
540 }
541
542 public Member get(int index) {
543 return members[index];
544 }
545
546 public Iterator<Member> iterator() {
547 return new Iterator<Member>() {
548 int index = 0;
549
550 public boolean hasNext() {
551 return (index < members.length);
552 }
553
554 public Member next() {
555 return members[index++];
556 }
557
558 public void remove() {
559 throw new UnsupportedOperationException("remove");
560 }
561 };
562 }
563 }
564
565 /**
566 * Each Position has two or more Members.
567 */
568 class PositionList extends PositionListBase {
569 PositionList() {
570 }
571
572 public boolean isEmpty() {
573 // may be considerably cheaper than computing size
574 return list.isEmpty();
575 }
576
577 public int size() {
578 return list.size();
579 }
580
581 public Position get(int index) {
582 return new MemberArrayListPosition(index);
583 }
584 }
585
586 /**
587 * Allows access only the the Member at the given offset.
588 */
589 class MemberArrayListPosition extends PositionBase {
590 protected final int offset;
591
592 MemberArrayListPosition(int offset) {
593 this.offset = offset;
594 }
595
596 public int size() {
597 return RolapAxis.MemberArrayIterable.this.len;
598 }
599
600 public Member get(int index) {
601 if (index > RolapAxis.MemberArrayIterable.this.len) {
602 throw new IndexOutOfBoundsException(
603 "Index: "
604 + index
605 + ", Size: "
606 + RolapAxis.MemberArrayIterable.this.len);
607 }
608 return list.get(offset)[index];
609 }
610 }
611 }
612
613 /**
614 * A MemberArrayList takes a List<Member[]> where each Position has
615 * the Member's from the corresponding location in the list.
616 * It is assumed that each element of the list has an array of Members of
617 * the same size.
618 */
619 public static class MemberArrayList extends RolapAxis {
620 private final List<Member[]> list;
621 private final int len;
622
623 public MemberArrayList(List<Member[]> list) {
624 this.list = list;
625 this.len = (list.size() == 0) ? 0 : list.get(0).length;
626 }
627
628 public List<Position> getPositions() {
629 return new MemberArrayList.PositionList();
630 }
631
632 /**
633 * Each Position has an array of Member.
634 */
635 class PositionList extends PositionListBase {
636 PositionList() {
637 }
638
639 public int size() {
640 return list.size();
641 }
642
643 public boolean isEmpty() {
644 // may be considerably cheaper than computing size
645 return list.isEmpty();
646 }
647
648 public Position get(int index) {
649 if (index >= list.size()) {
650 throw new IndexOutOfBoundsException();
651 }
652 return new MemberArrayListPosition(index);
653 }
654 }
655
656 /**
657 * Allows access only the the Member at the given offset plus index.
658 */
659 class MemberArrayListPosition extends PositionBase {
660 protected final int offset;
661
662 MemberArrayListPosition(int offset) {
663 this.offset = offset;
664 }
665
666 public int size() {
667 return RolapAxis.MemberArrayList.this.len;
668 }
669
670 public Member get(int index) {
671 if (index > RolapAxis.MemberArrayList.this.len) {
672 throw new IndexOutOfBoundsException(
673 "Index: "
674 + index
675 + ", Size: "
676 + RolapAxis.MemberArrayList.this.len);
677 }
678 return list.get(offset)[index];
679 }
680 }
681 }
682
683 /**
684 * A List<Member> for which all methods throw the
685 * UnsupportedOperationException exception when invoked. Derived classes
686 * can implement those methods that they require.
687 */
688 protected static abstract class PositionUnsupported
689 extends UnsupportedList<Member>
690 implements Position
691 {
692 protected PositionUnsupported() {
693 }
694 }
695
696 /**
697 * The PositionBase is an abstract implementation of the Position
698 * interface and provides both Iterator<Member> and
699 * ListIterator<Member> implementations.
700 */
701 protected static abstract class PositionBase
702 extends PositionUnsupported
703 {
704 protected PositionBase() {
705 }
706
707 public boolean equals(Object o) {
708 if (o == this) {
709 return true;
710 }
711 if (!(o instanceof List)) {
712 return false;
713 }
714 List that = (List) o;
715 final int size = this.size();
716 if (size != that.size()) {
717 return false;
718 }
719 for (int i = 0; i < size; i++) {
720 final Member m1 = get(i);
721 final Object m2 = that.get(i);
722 if (!(m1 == null
723 ? m2 == null
724 : m1.equals(m2)))
725 {
726 return false;
727 }
728 }
729 return true;
730 }
731
732 public int hashCode() {
733 int hashCode = 1;
734 int size = size();
735 for (int i = 0; i < size; i++) {
736 Member obj = get(i);
737 hashCode =
738 31 * hashCode + (obj == null
739 ? 0
740 : obj.hashCode());
741 }
742 return hashCode;
743 }
744
745 public ListIterator<Member> listIterator() {
746 return new ListItr(0);
747 }
748
749 public ListIterator<Member> listIterator(int index) {
750 return new ListItr(index);
751 }
752
753 public Iterator<Member> iterator() {
754 return new Itr();
755 }
756 }
757
758 protected static abstract class PositionListUnsupported
759 extends UnsupportedList<Position>
760 {
761 protected PositionListUnsupported() {
762 }
763 }
764
765 protected static abstract class PositionIterBase
766 extends PositionListUnsupported
767 {
768 protected PositionIterBase() {
769 }
770
771 public abstract Iterator<Position> iterator();
772 }
773
774 /**
775 * The PositionListBase is an abstract implementation of the
776 * List<Position>
777 * interface and provides both Iterator<Position> and
778 * ListIterator<Position> implementations.
779 */
780 protected static abstract class PositionListBase
781 extends PositionListUnsupported
782 {
783 protected PositionListBase() {
784 super();
785 }
786
787 public abstract int size();
788 public abstract boolean isEmpty();
789 public abstract Position get(int index);
790
791 // Collection
792 public ListIterator<Position> listIterator() {
793 return new ListItr(0);
794 }
795 public ListIterator<Position> listIterator(int index) {
796 return new ListItr(index);
797 }
798 public Iterator<Position> iterator() {
799 return new Itr();
800 }
801 }
802
803 protected RolapAxis() {
804 }
805
806 public abstract List<Position> getPositions();
807 }
808 // End RolapAxis.java