001package co.codewizards.cloudstore.core.collection;
002
003import static co.codewizards.cloudstore.core.util.Util.*;
004import static java.util.Objects.*;
005
006import java.lang.reflect.Array;
007import java.util.Collection;
008import java.util.Collections;
009import java.util.Iterator;
010import java.util.List;
011import java.util.ListIterator;
012
013/**
014 * View reversing a given {@link List}.
015 * <p>
016 * In contrast to {@link Collections#reverse(List)}, wrapping a {@code ReverseListView} around another
017 * {@link List} does not modify the wrapped {@link List}. It is fully backed by the original {@link List},
018 * hence every write operation is written through and modifications to the underlying {@link List} are
019 * immediately visible.
020 *
021 * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co
022 *
023 * @param <E> the element type.
024 */
025public final class ReverseListView<E> implements List<E> {
026        private final List<E> list;
027
028        public ReverseListView(final List<E> list) {
029                this.list = requireNonNull(list, "list");
030        }
031
032        @Override
033        public int size() {
034                return list.size();
035        }
036
037        @Override
038        public boolean isEmpty() {
039                return list.isEmpty();
040        }
041
042        @Override
043        public boolean contains(Object o) {
044                return list.contains(o);
045        }
046
047        @Override
048        public final Iterator<E> iterator() {
049                return listIterator(0);
050        }
051
052        @Override
053        public Object[] toArray() {
054                return toArray(new Object[size()]);
055        }
056
057        @SuppressWarnings("unchecked")
058        @Override
059        public <T> T[] toArray(T[] a) {
060                if (a.length < list.size())
061                        a = (T[]) Array.newInstance(a.getClass(), list.size());
062
063                int index = -1;
064                for (final E e : this)
065                        a[++index] = (T) e;
066
067                return a;
068        }
069
070        @Override
071        public boolean add(E e) {
072                list.add(0, e);
073                return true;
074        }
075
076        @Override
077        public boolean remove(Object o) {
078                return list.remove(o);
079        }
080
081        @Override
082        public boolean containsAll(Collection<?> c) {
083                return list.containsAll(c);
084        }
085
086        @Override
087        public boolean addAll(Collection<? extends E> c) {
088                if (c.isEmpty())
089                        return false;
090
091                for (E e : c)
092                        add(e);
093
094                return true;
095        }
096
097        @Override
098        public boolean addAll(int index, Collection<? extends E> c) {
099                if (c.isEmpty())
100                        return false;
101
102                for (E e : c)
103                        add(index++, e);
104
105                return true;
106        }
107
108        @Override
109        public boolean removeAll(Collection<?> c) {
110                return list.removeAll(c);
111        }
112
113        @Override
114        public boolean retainAll(Collection<?> c) {
115                return list.retainAll(c);
116        }
117
118        @Override
119        public void clear() {
120                list.clear();
121        }
122
123        @Override
124        public boolean equals(final Object o) {
125                if (o == this)
126                        return true;
127
128                if (! (o instanceof List))
129                        return false;
130
131                final List<?> other = (List<?>) o;
132                if (this.size() != other.size())
133                        return false;
134
135                final Iterator<E> thisIterator = this.iterator();
136                final Iterator<?> otherIterator = other.iterator();
137                while (thisIterator.hasNext() && otherIterator.hasNext()) {
138                        final E thisElement = thisIterator.next();
139                        final Object otherElement = otherIterator.next();
140                        if (! equal(thisElement, otherElement))
141                                return false;
142                }
143                return thisIterator.hasNext() == otherIterator.hasNext();
144        }
145
146        @Override
147        public int hashCode() {
148                int hashCode = 1;
149        for (E e : this)
150            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
151        return hashCode;
152        }
153
154        @Override
155        public E get(int index) {
156                return list.get(translateIndex(index));
157        }
158
159        @Override
160        public E set(int index, E element) {
161                return list.set(translateIndex(index), element);
162        }
163
164        @Override
165        public void add(int index, E element) {
166                list.add(translateIndex(index), element);
167        }
168
169        private int translateIndex(final int index) {
170                return list.size() - index - 1;
171        }
172
173        @Override
174        public E remove(int index) {
175                return list.remove(translateIndex(index));
176        }
177
178        @Override
179        public int indexOf(Object o) {
180                return translateIndex(list.lastIndexOf(o));
181        }
182
183        @Override
184        public int lastIndexOf(Object o) {
185                return translateIndex(list.indexOf(o));
186        }
187
188        @Override
189        public final ListIterator<E> listIterator() {
190                return listIterator(0);
191        }
192
193        @Override
194        public final ListIterator<E> listIterator(final int index) {
195                return new ListIterator<E>() {
196            private final ListIterator<E> listIter = list.listIterator(translateIndex(index) + 1);
197
198            @Override public boolean hasNext() { return listIter.hasPrevious(); }
199            @Override public E next() { return listIter.previous(); }
200            @Override public void remove() { listIter.remove(); }
201                        @Override public boolean hasPrevious() { return listIter.hasNext(); }
202                        @Override public E previous() { return listIter.next(); }
203                        @Override public int nextIndex() { return listIter.previousIndex(); }
204                        @Override public int previousIndex() { return listIter.nextIndex(); }
205                        @Override public void set(E e) { listIter.set(e); }
206
207                        @Override
208                        public void add(E e) {
209                                listIter.add(e);
210
211                                // According to javadoc, next() must be unaffected by add(...), while previous() must return the added element.
212                                // In order to comply with this contract, we must call this.next() (which is the delegate's previous()), now!
213                                if (e != next())
214                                        throw new IllegalStateException("WTF?!");
215                        }
216        };
217        }
218
219        @Override
220        public List<E> subList(int fromIndex, int toIndex) {
221                return new ReverseListView<E>(list.subList(translateIndex(toIndex), translateIndex(fromIndex)));
222        }
223
224        @Override
225        public String toString() {
226                final Iterator<E> it = iterator();
227                if (! it.hasNext())
228                        return "[]";
229
230                final StringBuilder sb = new StringBuilder();
231                sb.append('[');
232                for (;;) {
233                        final E e = it.next();
234                        sb.append(e == this || e == list ? "(this Collection)" : e);
235                        if (! it.hasNext())
236                                return sb.append(']').toString();
237                        sb.append(',').append(' ');
238                }
239        }
240}