001 /*
002 // $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RolapAxis.java#3 $
003 // This software is subject to the terms of the Common Public License
004 // Agreement, available at the following URL:
005 // http://www.opensource.org/licenses/cpl.html.
006 // Copyright (C) 2005-2007 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 import java.util.Collection;
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 import java.util.ListIterator;
026 import java.util.NoSuchElementException;
027
028 /**
029 * Derived classes of RolapAxis implements the Axis interface which are
030 * specializations based upon the number of Positions, how each Position's
031 * Members are orgainized and whether the Members/Member[]s are in a List
032 * or an Iterable.
033 *
034 * @author <a>Richard M. Emberson</a>
035 * @version $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RolapAxis.java#3 $
036 */
037 public abstract class RolapAxis implements Axis {
038 private static final Logger LOGGER = Logger.getLogger(RolapAxis.class);
039
040 public static String toString(Axis axis) {
041 List<Position> pl = axis.getPositions();
042 return toString(pl);
043 }
044 public static String toString(List<Position> pl) {
045 StringBuilder buf = new StringBuilder();
046 for (Position p: pl) {
047 buf.append('{');
048 boolean firstTime = true;
049 for (Member m: p) {
050 if (! firstTime) {
051 buf.append(", ");
052 }
053 buf.append(m.getUniqueName());
054 firstTime = false;
055 }
056 buf.append('}');
057 buf.append('\n');
058 }
059 return buf.toString();
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 //System.out.println("RolapAxis.materialize: 1");
152 if (list == null) {
153 Iterator<Member> it = iter.iterator();
154 list = new ArrayList<Member>();
155 while (it.hasNext()) {
156 list.add(it.next());
157 }
158 // allow gc of iter
159 iter = null;
160 }
161 }
162
163
164 /**
165 * This List<Position> starts life with a List<Position>
166 * implementation
167 * that is based upon an non-List (Iterable). If all accesses
168 * are simply through iteration, then the initial implementation
169 * remains, but if the client uses either the 'size' or 'get' methods
170 * then the Iterable is materialized into a List.
171 */
172 class PositionWrapper extends PositionListUnsupported {
173 List<Position> positionList;
174 PositionWrapper() {
175 positionList = new PositionIter();
176 }
177 protected synchronized void materialize() {
178 //System.out.println("RolapAxis.materialize: 2");
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 public int size() {
188 try {
189 return positionList.size();
190 } catch (UnsupportedOperationException ex) {
191 this.materialize();
192 return positionList.size();
193 }
194 }
195 public Position get(int index) {
196 try {
197 return positionList.get(index);
198 } catch (UnsupportedOperationException ex) {
199 this.materialize();
200 return positionList.get(index);
201 }
202 }
203 public Iterator<Position> iterator() {
204 return positionList.iterator();
205 }
206 }
207
208 /**
209 * PositionIter is a List<Position> that only support the
210 * 'iterator' method. This assumes that one iterates over Positions
211 * and for each Postion one iterates over Members. In this case,
212 * each Position has a single Member.
213 */
214 class PositionIter extends PositionIterBase {
215 private Iterator<Member> it;
216 PositionIter() {
217 it = iter.iterator();
218 }
219 public Iterator<Position> iterator() {
220 return new Iterator<Position>() {
221 public boolean hasNext() {
222 return it.hasNext();
223 }
224 public Position next() {
225 return new MemberIterable.MIPosition(it.next());
226 }
227 public void remove() {
228 throw new UnsupportedOperationException("remove");
229 }
230 };
231 }
232 }
233
234 /**
235 * A List<Member> which only implements the 'iterator' method.
236 * Each Iterator<Member> has only one Member.
237 */
238 class MIPosition extends PositionBase {
239 Member member;
240 MIPosition(Member member) {
241 this.member = member;
242 }
243 public int size() {
244 return 1;
245 }
246 public Member get(int index) {
247 if (index != 0) {
248 throw new IndexOutOfBoundsException(
249 "Index: "+index+", Size: 1");
250 }
251 return member;
252 }
253
254 public Iterator<Member> iterator() {
255 return new Iterator<Member>() {
256 public boolean hasNext() {
257 return (member != null);
258 }
259 public Member next() {
260 try {
261 return member;
262 } finally {
263 member = null;
264 }
265 }
266 public void remove() {
267 throw new UnsupportedOperationException("remove");
268 }
269 };
270 }
271 }
272
273 /**
274 * Each Position has a single Member.
275 */
276 class PositionList extends PositionListBase {
277 PositionList() {
278 }
279 public int size() {
280 return list.size();
281 }
282 public Position get(int index) {
283 return new MemberIterable.MLPosition(index);
284 }
285 }
286
287 /**
288 * Allows access only the the Member at the given offset.
289 */
290 class MLPosition extends PositionBase {
291 protected final int offset;
292 MLPosition(int offset) {
293 this.offset = offset;
294 }
295 public int size() {
296 return 1;
297 }
298 public Member get(int index) {
299 if (index != 0) {
300 throw new IndexOutOfBoundsException(
301 "Index: "+index+", Size: 1");
302 }
303 return list.get(offset);
304 }
305 }
306 }
307
308
309
310 /**
311 * A MemberList takes a List<Member> where each Position has
312 * a single Member from the corresponding location in the list.
313 */
314 public static class MemberList extends RolapAxis {
315 private final List<Member> list;
316 public MemberList(List<Member> list) {
317 this.list = list;
318 }
319 public List<Position> getPositions() {
320 return new MemberList.PositionList();
321 }
322 /**
323 * Each Position has a single Member.
324 */
325 class PositionList extends PositionListBase {
326 PositionList() {
327 }
328 public int size() {
329 return list.size();
330 }
331 public Position get(int index) {
332 return new MemberList.MLPosition(index);
333 }
334 }
335
336 /**
337 * Allows access only the the Member at the given offset.
338 */
339 class MLPosition extends PositionBase {
340 protected final int offset;
341 MLPosition(int offset) {
342 this.offset = offset;
343 }
344 public int size() {
345 return 1;
346 }
347 public Member get(int index) {
348 if (index != 0) {
349 throw new IndexOutOfBoundsException(
350 "Index: "+index+", Size: 1");
351 }
352 return list.get(offset);
353 }
354 }
355 }
356
357 /**
358 * A MemberArrayIterable takes an Iterable<Member[]> where
359 * each Position has
360 * an array of Members from the corresponding location in the iterator.
361 * If the client request any of the List, non-Iterable, API, then
362 * a List is materialized from the Iterable.
363 */
364 public static class MemberArrayIterable extends RolapAxis {
365 private Iterable<Member[]> iter;
366 private List<Member[]> list;
367 private int len;
368 public MemberArrayIterable(Iterable<Member[]> iter) {
369 this.iter = iter;
370 this.list = null;
371 this.len = 0;
372 }
373 public synchronized List<Position> getPositions() {
374 return (list == null)
375 ? new MemberArrayIterable.PositionWrapper()
376 : new MemberArrayIterable.PositionList();
377 }
378 protected synchronized void materialize() {
379 //System.out.println("RolapAxis.materialize: 3");
380 if (list == null) {
381 Iterator<Member[]> it = iter.iterator();
382 list = new ArrayList<Member[]>();
383 while (it.hasNext()) {
384 list.add(it.next());
385 }
386 // allow gc of iter
387 iter = null;
388
389 len = (list.size() == 0) ? 0 : list.get(0).length;
390 }
391 }
392
393 /**
394 * This List<Position> starts life with a List<Position>
395 * implementation
396 * that is based upon an non-List (Iterable). If all accesses
397 * are simply through iteration, then the initial implementation
398 * remains, but if the client uses either the 'size' or 'get' methods
399 * then the Iterable is materialized into a List.
400 */
401 class PositionWrapper extends PositionListUnsupported {
402 List<Position> positionList;
403 PositionWrapper() {
404 positionList = new PositionIter();
405 }
406 protected synchronized void materialize() {
407 //System.out.println("RolapAxis.materialize: 4");
408 if (LOGGER.isDebugEnabled()) {
409 LOGGER.debug(
410 "PositionWrapper.materialize: Member[] iter.class="
411 + ((iter != null) ? iter.getClass().getName() : null));
412 }
413 RolapAxis.MemberArrayIterable.this.materialize();
414 positionList = new MemberArrayIterable.PositionList();
415 }
416 public int size() {
417 try {
418 return positionList.size();
419 } catch (UnsupportedOperationException ex) {
420 this.materialize();
421 return positionList.size();
422 }
423 }
424 public Position get(int index) {
425 try {
426 return positionList.get(index);
427 } catch (UnsupportedOperationException ex) {
428 this.materialize();
429 return positionList.get(index);
430 }
431 }
432 public Iterator<Position> iterator() {
433 return positionList.iterator();
434 }
435 }
436
437 /**
438 * PositionIter is a List<Position> that only support the
439 * 'iterator' method. This assumes that one iterates over Positions
440 * and for each Postion one iterates over Members. Each Position
441 * has two or more Members.
442 */
443 class PositionIter extends PositionIterBase {
444 private Iterator<Member[]> it;
445 PositionIter() {
446 it = iter.iterator();
447 }
448 public Iterator<Position> iterator() {
449 return new Iterator<Position>() {
450 int nextCnt = 0;
451 public boolean hasNext() {
452 return it.hasNext();
453 }
454 public Position next() {
455 nextCnt++;
456 return new MemberArrayIterable.MIPosition(it.next());
457 }
458 public void remove() {
459 throw new UnsupportedOperationException("remove");
460 }
461 };
462 }
463 }
464 /**
465 * A List<Member> which only implements the 'iterator' method.
466 * Each Iterator<Member> two or more Members.
467 */
468 class MIPosition extends PositionBase {
469 Member[] members;
470 MIPosition(Member[] members) {
471 this.members = members;
472 }
473 public int size() {
474 return members.length;
475 }
476 public Member get(int index) {
477 return members[index];
478 }
479 public Iterator<Member> iterator() {
480 return new Iterator<Member>() {
481 int index = 0;
482 public boolean hasNext() {
483 return (index < members.length);
484 }
485 public Member next() {
486 return members[index++];
487 }
488 public void remove() {
489 throw new UnsupportedOperationException("remove");
490 }
491 };
492 }
493 }
494
495 /**
496 * Each Position has two or more Members.
497 */
498 class PositionList extends PositionListBase {
499 PositionList() {
500 }
501 public int size() {
502 return list.size();
503 }
504 public Position get(int index) {
505 return new MemberArrayIterable.MALPosition(index);
506 }
507 }
508
509 /**
510 * Allows access only the the Member at the given offset.
511 */
512 class MALPosition extends PositionBase {
513 protected final int offset;
514 MALPosition(int offset) {
515 this.offset = offset;
516 }
517 public int size() {
518 return RolapAxis.MemberArrayIterable.this.len;
519 }
520 public Member get(int index) {
521 if (index > RolapAxis.MemberArrayIterable.this.len) {
522 throw new IndexOutOfBoundsException(
523 "Index: " +
524 index +
525 ", Size: " +
526 RolapAxis.MemberArrayIterable.this.len);
527 }
528 return list.get(offset)[index];
529 }
530 }
531 }
532
533
534
535 /**
536 * A MemberArrayList takes a List<Member[]> where each Position has
537 * the Member's from the corresponding location in the list.
538 * It is assumed that each element of the list has an array of Members of
539 * the same size.
540 */
541 public static class MemberArrayList extends RolapAxis {
542 private final List<Member[]> list;
543 private final int len;
544 public MemberArrayList(List<Member[]> list) {
545 this.list = list;
546 this.len = (list.size() == 0) ? 0 : list.get(0).length;
547 }
548 public List<Position> getPositions() {
549 return new MemberArrayList.PositionList();
550 }
551 /**
552 * Each Position has an array of Member.
553 */
554 class PositionList extends PositionListBase {
555 PositionList() {
556 }
557 public int size() {
558 return list.size();
559 }
560 public Position get(int index) {
561 return new MemberArrayList.MALPosition(index);
562 }
563 }
564 /**
565 * Allows access only the the Member at the given offset plus index.
566 */
567 class MALPosition extends PositionBase {
568 protected final int offset;
569 MALPosition(int offset) {
570 this.offset = offset;
571 }
572 public int size() {
573 return RolapAxis.MemberArrayList.this.len;
574 }
575 public Member get(int index) {
576 if (index > RolapAxis.MemberArrayList.this.len) {
577 throw new IndexOutOfBoundsException(
578 "Index: " +
579 index +
580 ", Size: " +
581 RolapAxis.MemberArrayList.this.len);
582 }
583 return list.get(offset)[index];
584 }
585 }
586 }
587
588 /**
589 * A List<Member> for which all methods throw the
590 * UnsupportedOperationException exception when invoked. Derived classes
591 * can implement those methods that they require.
592 */
593 protected static abstract class PositionUnsupported
594 extends UnsupportedList<Member>
595 implements Position {
596 protected PositionUnsupported() {
597 }
598 }
599 /**
600 * The PositionBase is an abstract implementation of the Position
601 * interface and provides both Iterator<Member> and
602 * ListIterator<Member> implementations.
603 */
604 protected static abstract class PositionBase extends PositionUnsupported {
605 protected PositionBase() {
606 }
607 public ListIterator<Member> listIterator() {
608 return new ListItr(0);
609 }
610 public ListIterator<Member> listIterator(int index) {
611 return new ListItr(index);
612 }
613 public Iterator<Member> iterator() {
614 return new Itr();
615 }
616 }
617
618 protected static abstract class PositionListUnsupported
619 extends UnsupportedList<Position> {
620 protected PositionListUnsupported() {
621 }
622 }
623
624 protected static abstract class PositionIterBase
625 extends PositionListUnsupported {
626 protected PositionIterBase() {
627 super();
628 }
629 public abstract Iterator<Position> iterator();
630 }
631
632 /**
633 * The PositionListBase is an abstract implementation of the
634 * List<Position>
635 * interface and provides both Iterator<Position> and
636 * ListIterator<Position> implementations.
637 */
638 protected static abstract class PositionListBase
639 extends PositionListUnsupported {
640 protected PositionListBase() {
641 super();
642 }
643 public abstract int size();
644 public abstract Position get(int index);
645
646 // Collection
647 public boolean isEmpty() {
648 return (size() == 0);
649 }
650 public ListIterator<Position> listIterator() {
651 return new ListItr(0);
652 }
653 public ListIterator<Position> listIterator(int index) {
654 return new ListItr(index);
655 }
656 public Iterator<Position> iterator() {
657 return new Itr();
658 }
659 }
660
661 protected RolapAxis() {
662 }
663 public abstract List<Position> getPositions();
664 }
665 // End RolapAxis.java