001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RolapAxis.java#19 $
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-2008 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/src/main/mondrian/rolap/RolapAxis.java#19 $
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&lt;Member&gt; 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&lt;Position&gt; starts life with a List&lt;Position&gt;
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&lt;Position&gt; 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&lt;Member&gt; which only implements the 'iterator' method.
236             * Each Iterator&lt;Member&gt; 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&lt;Member&gt; 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                public Iterator<Position> iterator() {
335                    return new Iterator<Position>() {
336                        private final Iterator it = list.iterator();
337                        private int cursor = 0;
338                        public boolean hasNext() {
339                            return it.hasNext();
340                        }
341                        public Position next() {
342                            it.next();
343                            return get(cursor++);
344                        }
345                        public void remove() {
346                            throw new UnsupportedOperationException();
347                        }
348                    };
349                }
350            }
351    
352            /**
353             *  Allows access only the the Member at the given offset.
354             */
355            class MLPosition extends PositionBase {
356                protected final int offset;
357                MLPosition(int offset) {
358                    this.offset = offset;
359                }
360                public int size() {
361                    return 1;
362                }
363                public Member get(int index) {
364                    if (index != 0) {
365                        throw new IndexOutOfBoundsException(
366                            "Index: " + index + ", Size: 1");
367                    }
368                    return list.get(offset);
369                }
370            }
371        }
372    
373        /**
374         * A MemberArrayIterable takes an Iterable&lt;Member[]&gt; where
375         * each Position has
376         * an array of Members from the corresponding location in the iterator.
377         * If the client request any of the List, non-Iterable, API, then
378         * a List is materialized from the Iterable.
379         */
380        public static class MemberArrayIterable extends RolapAxis {
381            private Iterable<Member[]> iter;
382            private List<Member[]> list;
383            private int len;
384            public MemberArrayIterable(Iterable<Member[]> iter) {
385                this.iter = iter;
386                this.list = null;
387                this.len = 0;
388            }
389            public synchronized List<Position> getPositions() {
390                return (list == null)
391                    ? new MemberArrayIterable.PositionWrapper()
392                    : new MemberArrayIterable.PositionList();
393            }
394            protected synchronized void materialize() {
395    //System.out.println("RolapAxis.materialize: 3");
396                if (list == null) {
397                    Iterator<Member[]> it = iter.iterator();
398                    list = new ArrayList<Member[]>();
399                    while (it.hasNext()) {
400                        list.add(it.next());
401                    }
402                    // allow gc of iter
403                    iter = null;
404    
405                    len = (list.size() == 0) ? 0 : list.get(0).length;
406                }
407            }
408    
409            /**
410             * This List&lt;Position&gt; starts life with a List&lt;Position&gt;
411             * implementation
412             * that is based upon an non-List (Iterable). If all accesses
413             * are simply through iteration, then the initial implementation
414             * remains, but if the client uses either the 'size' or 'get' methods
415             * then the Iterable is materialized into a List.
416             */
417            class PositionWrapper extends PositionListUnsupported {
418                List<Position> positionList;
419                PositionWrapper() {
420                    positionList = new PositionIter();
421                }
422                protected synchronized void materialize() {
423    //System.out.println("RolapAxis.materialize: 4");
424                    if (LOGGER.isDebugEnabled()) {
425                        LOGGER.debug(
426                            "PositionWrapper.materialize: Member[] iter.class="
427                            + ((iter != null) ? iter.getClass().getName() : null));
428                    }
429                    RolapAxis.MemberArrayIterable.this.materialize();
430                    positionList = new MemberArrayIterable.PositionList();
431                }
432                public int size() {
433                    try {
434                        return positionList.size();
435                    } catch (UnsupportedOperationException ex) {
436                        this.materialize();
437                        return positionList.size();
438                    }
439                }
440                public Position get(int index) {
441                    try {
442                        return positionList.get(index);
443                    } catch (UnsupportedOperationException ex) {
444                        this.materialize();
445                        return positionList.get(index);
446                    }
447                }
448                public Iterator<Position> iterator() {
449                    return positionList.iterator();
450                }
451            }
452    
453            /**
454             * PositionIter is a List&lt;Position&gt; that only support the
455             * 'iterator' method. This assumes that one iterates over Positions
456             * and for each Postion one iterates over Members. Each Position
457             * has two or more Members.
458             */
459            class PositionIter extends PositionIterBase {
460                private Iterator<Member[]> it;
461                PositionIter() {
462                    it = iter.iterator();
463                }
464                public Iterator<Position> iterator() {
465                    return new Iterator<Position>() {
466                        int nextCnt = 0;
467                        public boolean hasNext() {
468                            return it.hasNext();
469                        }
470                        public Position next() {
471                            nextCnt++;
472                            return new MemberArrayIterable.MIPosition(it.next());
473                        }
474                        public void remove() {
475                            throw new UnsupportedOperationException("remove");
476                        }
477                    };
478                }
479            }
480            /**
481             * A List&lt;Member&gt; which only implements the 'iterator' method.
482             * Each Iterator&lt;Member&gt; two or more Members.
483             */
484            class MIPosition extends PositionBase  {
485                Member[] members;
486                MIPosition(Member[] members) {
487                    this.members = members;
488                }
489                public int size() {
490                    return members.length;
491                }
492                public Member get(int index) {
493                    return members[index];
494                }
495                public Iterator<Member> iterator() {
496                    return new Iterator<Member>() {
497                        int index = 0;
498                        public boolean hasNext() {
499                            return (index < members.length);
500                        }
501                        public Member next() {
502                            return members[index++];
503                        }
504                        public void remove() {
505                            throw new UnsupportedOperationException("remove");
506                        }
507                    };
508                }
509            }
510    
511            /**
512             *  Each Position has two or more Members.
513             */
514            class PositionList extends PositionListBase {
515                PositionList() {
516                }
517                public int size() {
518                    return list.size();
519                }
520                public Position get(int index) {
521                    return new MemberArrayIterable.MALPosition(index);
522                }
523            }
524    
525            /**
526             *  Allows access only the the Member at the given offset.
527             */
528            class MALPosition extends PositionBase {
529                protected final int offset;
530                MALPosition(int offset) {
531                    this.offset = offset;
532                }
533                public int size() {
534                    return RolapAxis.MemberArrayIterable.this.len;
535                }
536                public Member get(int index) {
537                    if (index > RolapAxis.MemberArrayIterable.this.len) {
538                        throw new IndexOutOfBoundsException(
539                            "Index: " +
540                            index +
541                            ", Size: " +
542                            RolapAxis.MemberArrayIterable.this.len);
543                    }
544                    return list.get(offset)[index];
545                }
546            }
547        }
548    
549    
550    
551        /**
552         * A MemberArrayList takes a List&lt;Member[]&gt; where each Position has
553         * the Member's from the corresponding location in the list.
554         * It is assumed that each element of the list has an array of Members of
555         * the same size.
556         */
557        public static class MemberArrayList extends RolapAxis {
558            private final List<Member[]> list;
559            private final int len;
560            public MemberArrayList(List<Member[]> list) {
561                this.list = list;
562                this.len = (list.size() == 0) ? 0 : list.get(0).length;
563            }
564            public List<Position> getPositions() {
565                return new MemberArrayList.PositionList();
566            }
567            /**
568             *  Each Position has an array of Member.
569             */
570            class PositionList extends PositionListBase {
571                PositionList() {
572                }
573                public int size() {
574                    return list.size();
575                }
576                public Position get(int index) {
577                    if (index >= list.size()) {
578                        throw new IndexOutOfBoundsException();
579                    }
580                    return new MemberArrayList.MALPosition(index);
581                }
582            }
583            /**
584             *  Allows access only the the Member at the given offset plus index.
585             */
586            class MALPosition extends PositionBase {
587                protected final int offset;
588                MALPosition(int offset) {
589                    this.offset = offset;
590                }
591                public int size() {
592                    return RolapAxis.MemberArrayList.this.len;
593                }
594                public Member get(int index) {
595                    if (index > RolapAxis.MemberArrayList.this.len) {
596                        throw new IndexOutOfBoundsException(
597                            "Index: " +
598                            index +
599                            ", Size: " +
600                            RolapAxis.MemberArrayList.this.len);
601                    }
602                    return list.get(offset)[index];
603                }
604            }
605        }
606    
607        /**
608         * A List&lt;Member&gt; for which all methods throw the
609         * UnsupportedOperationException exception when invoked. Derived classes
610         * can implement those methods that they require.
611         */
612        protected static abstract class PositionUnsupported
613                            extends UnsupportedList<Member>
614                            implements Position {
615            protected PositionUnsupported() {
616            }
617        }
618        /**
619         * The PositionBase is an abstract implementation of the Position
620         * interface and provides both Iterator&lt;Member&gt; and
621         * ListIterator&lt;Member&gt; implementations.
622         */
623        protected static abstract class PositionBase extends PositionUnsupported {
624            protected PositionBase() {
625            }
626            public ListIterator<Member> listIterator() {
627                return new ListItr(0);
628            }
629            public ListIterator<Member> listIterator(int index) {
630                return new ListItr(index);
631            }
632            public Iterator<Member> iterator() {
633                return new Itr();
634            }
635        }
636    
637        protected static abstract class PositionListUnsupported
638                            extends UnsupportedList<Position> {
639            protected PositionListUnsupported() {
640            }
641        }
642    
643        protected static abstract class PositionIterBase
644                                    extends PositionListUnsupported {
645            protected PositionIterBase() {
646                super();
647            }
648            public abstract Iterator<Position> iterator();
649        }
650    
651        /**
652         * The PositionListBase is an abstract implementation of the
653         * List&lt;Position&gt
654         * interface and provides both Iterator&lt;Position&gt; and
655         * ListIterator&lt;Position&gt; implementations.
656         */
657        protected static abstract class PositionListBase
658                                    extends PositionListUnsupported {
659            protected PositionListBase() {
660                super();
661            }
662            public abstract int size();
663            public abstract Position get(int index);
664    
665            // Collection
666            public boolean isEmpty() {
667                return (size() == 0);
668            }
669            public ListIterator<Position> listIterator() {
670                return new ListItr(0);
671            }
672            public ListIterator<Position> listIterator(int index) {
673                return new ListItr(index);
674            }
675            public Iterator<Position> iterator() {
676                return new Itr();
677            }
678        }
679    
680        protected RolapAxis() {
681        }
682        public abstract List<Position> getPositions();
683    }
684    // End RolapAxis.java