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&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            }
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&lt;Member[]&gt; 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&lt;Position&gt; starts life with a List&lt;Position&gt;
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&lt;Position&gt; 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&lt;Member&gt; which only implements the 'iterator' method.
466             * Each Iterator&lt;Member&gt; 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&lt;Member[]&gt; 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&lt;Member&gt; 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&lt;Member&gt; and
602         * ListIterator&lt;Member&gt; 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&lt;Position&gt
635         * interface and provides both Iterator&lt;Position&gt; and
636         * ListIterator&lt;Position&gt; 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