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&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                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&lt;Position&gt; starts life with a List&lt;Position&gt;
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&lt;Position&gt; 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&lt;Member&gt; which only implements the 'iterator' method.
243             * Each Iterator&lt;Member&gt; 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&lt;Member&gt; 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&lt;Member[]&gt; 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&lt;Position&gt; starts life with a List&lt;Position&gt;
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&lt;Position&gt; 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&lt;Member&gt; which only implements the 'iterator' method.
529             * Each Iterator&lt;Member&gt; 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&lt;Member[]&gt; 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&lt;Member&gt; 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&lt;Member&gt; and
699         * ListIterator&lt;Member&gt; 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&lt;Position&gt
777         * interface and provides both Iterator&lt;Position&gt; and
778         * ListIterator&lt;Position&gt; 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