001    /*
002    // $Id: //open/mondrian/src/main/mondrian/util/ConcatenableList.java#5 $
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) 2006-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.util;
011    
012    import java.util.*;
013    
014    /**
015     * @author Luis F. Canals
016     * @version $Id: //open/mondrian/src/main/mondrian/util/ConcatenableList.java#5 $
017     * @since december, 2007
018     */
019    public class ConcatenableList<T> extends AbstractList<T> {
020        private static int nextHashCode = 1000;
021        private final List<List<T>> lists;
022        private List<T> plainList;
023        private final int hashCode = nextHashCode++;
024        private Iterator<T> getIterator = null;
025        private int previousIndex = -200;
026        private T previousElement = null;
027        private T prePreviousElement = null;
028    
029        public ConcatenableList() {
030            this.lists = new ArrayList<List<T>>();
031            this.plainList = null;
032        }
033    
034    
035        /**
036         * Performs a load of all elements into memory, removing sequential
037         * access advantages.
038         */
039        public Object[] toArray() {
040            if (this.plainList == null) {
041                this.plainList = new ArrayList<T>();
042                for (final List<T> list : lists) {
043                    for (final T t : list) {
044                        this.plainList.add(t);
045                    }
046                }
047            }
048            return this.plainList.toArray();
049        }
050    
051        public boolean addAll(final Collection<? extends T> collection) {
052            if (this.plainList == null) {
053                final List<T> list = (List<T>) collection;
054                return this.lists.add(list);
055            } else {
056                for (final T e : collection) {
057                    this.plainList.add(e);
058                }
059                return true;
060            }
061        }
062    
063        public T get(final int index) {
064            if (this.plainList == null) {
065                if (index == 0) {
066                    this.getIterator = this.iterator();
067                    this.previousIndex = index;
068                    if (this.getIterator.hasNext()) {
069                        this.previousElement = this.getIterator.next();
070                        return this.previousElement;
071                    } else {
072                        this.getIterator = null;
073                        this.previousIndex = -200;
074                        throw new IndexOutOfBoundsException("Index " + index
075                                + " out of concatenable list range");
076                    }
077                } else if (this.previousIndex + 1 == index && this.getIterator != null) {
078                    this.previousIndex = index;
079                    if (this.getIterator.hasNext()) {
080                        this.prePreviousElement = this.previousElement;
081                        this.previousElement = this.getIterator.next();
082                        return this.previousElement;
083                    } else {
084                        this.getIterator = null;
085                        this.previousIndex = -200;
086                        throw new IndexOutOfBoundsException("Index " + index
087                                + " out of concatenable list range");
088                    }
089                } else if (this.previousIndex == index) {
090                    return this.previousElement;
091                } else if (this.previousIndex - 1 == index) {
092                    return this.prePreviousElement;
093                } else {
094                    this.previousIndex = -200;
095                    this.getIterator = null;
096                    final Iterator<T> it = this.iterator();
097                    if (!it.hasNext()) {
098                        throw new IndexOutOfBoundsException("Index " + index
099                                + " out of concatenable list range");
100                    }
101                    for (int i = 0; i < index; i++) {
102                        if (!it.hasNext()) {
103                            throw new IndexOutOfBoundsException("Index " + index
104                                + " out of concatenable list range");
105                        }
106                        this.prePreviousElement = it.next();
107                    }
108                    this.previousElement = it.next();
109                    this.previousIndex = index;
110                    this.getIterator = it;
111                    return this.previousElement;
112                }
113            } else {
114                this.previousElement = this.plainList.get(index);
115                return this.previousElement;
116            }
117        }
118    
119    
120        /**
121         * Adds elements at the end of the list.
122         */
123        public boolean add(final T t) {
124            if (this.plainList == null) {
125                return this.addAll(Collections.singletonList(t));
126            } else {
127                return this.plainList.add(t);
128            }
129        }
130    
131        /**
132         * Adds elements at the middle of the list.
133         */
134        public void add(final int index, final T t) {
135            if (this.plainList == null) {
136                throw new UnsupportedOperationException();
137            } else {
138                this.plainList.add(index, t);
139            }
140        }
141    
142    
143    
144        /**
145         * Burnt method!
146         */
147        public T set(final int index, final T t) {
148            if (this.plainList == null) {
149                throw new UnsupportedOperationException();
150            } else {
151                return this.plainList.set(index, t);
152            }
153        }
154    
155        public int size() {
156            if (this.plainList == null) {
157                int size = 0;
158                for (final List<T> list : lists) {
159                    size += list.size();
160                }
161                return size;
162            } else {
163                return this.plainList.size();
164            }
165        }
166    
167        public Iterator<T> iterator() {
168            if (this.plainList == null) {
169                return new Iterator<T>() {
170                    private final Iterator<List<T>> listsIt=lists.iterator();
171                    private Iterator<T> currentListIt;
172    
173                    public boolean hasNext() {
174                        if (currentListIt == null) {
175                            if (listsIt.hasNext()) {
176                                currentListIt = listsIt.next().iterator();
177                            } else {
178                                return false;
179                            }
180                        }
181    
182                        if (currentListIt.hasNext()) {
183                            return true;
184                        } else {
185                            if (listsIt.hasNext()) {
186                                currentListIt = listsIt.next().iterator();
187                                return currentListIt.hasNext();
188                            } else {
189                                return false;
190                            }
191                        }
192                    }
193    
194                    public T next() {
195                        if (currentListIt.hasNext()) {
196                            return currentListIt.next();
197                        } else {
198                            currentListIt = listsIt.next().iterator();
199                            return currentListIt.next();
200                        }
201                    }
202    
203                    public void remove() {
204                        throw new UnsupportedOperationException();
205                    }
206                };
207            } else {
208                return this.plainList.iterator();
209            }
210        }
211    
212        public boolean isEmpty() {
213            if (this.plainList != null) {
214                return this.plainList.isEmpty();
215            }
216            if (this.lists.isEmpty()) {
217                return true;
218            } else {
219                for (final List<T> l : lists) {
220                    if (!l.isEmpty()) {
221                        return false;
222                    }
223                }
224                return true;
225            }
226        }
227    
228        public void clear() {
229            this.plainList = null;
230            this.lists.clear();
231        }
232    
233        public int hashCode() {
234            return this.hashCode;
235        }
236    }
237    
238    // End ConcatenableList.java