1/*
2 * Copyright (C) 2009 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.collect.Lists;
22
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.Iterator;
28import java.util.List;
29import java.util.ListIterator;
30import java.util.RandomAccess;
31
32import javax.annotation.Nullable;
33
34/**
35 * GWT emulated version of {@link ImmutableList}.
36 *
37 * @author Hayward Chan
38 */
39@SuppressWarnings("serial") // we're overriding default serialization
40public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
41    implements List<E>, RandomAccess {
42
43  private transient final List<E> delegate;
44
45  ImmutableList(List<E> delegate) {
46    super(delegate);
47    this.delegate = Collections.unmodifiableList(delegate);
48  }
49
50  ImmutableList() {
51    this(Collections.<E>emptyList());
52  }
53
54  // Casting to any type is safe because the list will never hold any elements.
55  @SuppressWarnings("unchecked")
56  public static <E> ImmutableList<E> of() {
57    return (ImmutableList<E>) EmptyImmutableList.INSTANCE;
58  }
59
60  public static <E> ImmutableList<E> of(E element) {
61    return new SingletonImmutableList<E>(element);
62  }
63
64  public static <E> ImmutableList<E> of(E e1, E e2) {
65    return new RegularImmutableList<E>(
66        ImmutableList.<E>nullCheckedList(e1, e2));
67  }
68
69  public static <E> ImmutableList<E> of(E e1, E e2, E e3) {
70    return new RegularImmutableList<E>(
71        ImmutableList.<E>nullCheckedList(e1, e2, e3));
72  }
73
74  public static <E> ImmutableList<E> of(E e1, E e2, E e3, E e4) {
75    return new RegularImmutableList<E>(
76        ImmutableList.<E>nullCheckedList(e1, e2, e3, e4));
77  }
78
79  public static <E> ImmutableList<E> of(E e1, E e2, E e3, E e4, E e5) {
80    return new RegularImmutableList<E>(
81        ImmutableList.<E>nullCheckedList(e1, e2, e3, e4, e5));
82  }
83
84  public static <E> ImmutableList<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
85    return new RegularImmutableList<E>(
86        ImmutableList.<E>nullCheckedList(e1, e2, e3, e4, e5, e6));
87  }
88
89  public static <E> ImmutableList<E> of(
90      E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
91    return new RegularImmutableList<E>(
92         ImmutableList.<E>nullCheckedList(e1, e2, e3, e4, e5, e6, e7));
93  }
94
95  public static <E> ImmutableList<E> of(
96      E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
97    return new RegularImmutableList<E>(
98         ImmutableList.<E>nullCheckedList(e1, e2, e3, e4, e5, e6, e7, e8));
99  }
100
101  public static <E> ImmutableList<E> of(
102      E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
103    return new RegularImmutableList<E>(
104         ImmutableList.<E>nullCheckedList(e1, e2, e3, e4, e5, e6, e7, e8, e9));
105  }
106
107  public static <E> ImmutableList<E> of(
108      E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
109    return new RegularImmutableList<E>(ImmutableList.<E>nullCheckedList(
110        e1, e2, e3, e4, e5, e6, e7, e8, e9, e10));
111  }
112
113  public static <E> ImmutableList<E> of(
114      E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11) {
115    return new RegularImmutableList<E>(ImmutableList.<E>nullCheckedList(
116        e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11));
117  }
118
119  public static <E> ImmutableList<E> of(
120      E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11,
121      E e12, E... others) {
122    final int paramCount = 12;
123    Object[] array = new Object[paramCount + others.length];
124    arrayCopy(array, 0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12);
125    arrayCopy(array, paramCount, others);
126    return new RegularImmutableList<E>(ImmutableList.<E>nullCheckedList(array));
127  }
128
129  public static <E> ImmutableList<E> of(E[] elements) {
130    checkNotNull(elements); // for GWT
131    switch (elements.length) {
132      case 0:
133        return ImmutableList.of();
134      case 1:
135        return new SingletonImmutableList<E>(elements[0]);
136      default:
137        return new RegularImmutableList<E>(
138            ImmutableList.<E>nullCheckedList(elements));
139    }
140  }
141
142  private static void arrayCopy(Object[] dest, int pos, Object... source) {
143    System.arraycopy(source, 0, dest, pos, source.length);
144  }
145
146  public static <E> ImmutableList<E> copyOf(Iterable<? extends E> elements) {
147    checkNotNull(elements); // for GWT
148    return (elements instanceof Collection)
149        ? copyOf((Collection<? extends E>) elements)
150        : copyOf(elements.iterator());
151  }
152
153  public static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements) {
154    return copyFromCollection(Lists.newArrayList(elements));
155  }
156
157  public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {
158    if (elements instanceof ImmutableCollection) {
159      /*
160       * TODO: When given an ImmutableList that's a sublist, copy the referenced
161       * portion of the array into a new array to save space?
162       */
163      @SuppressWarnings("unchecked") // all supported methods are covariant
164      ImmutableCollection<E> list = (ImmutableCollection<E>) elements;
165      return list.asList();
166    }
167    return copyFromCollection(elements);
168  }
169
170  public static <E> ImmutableList<E> copyOf(E[] elements) {
171    checkNotNull(elements); // eager for GWT
172    return copyOf(Arrays.asList(elements));
173  }
174
175  private static <E> ImmutableList<E> copyFromCollection(
176      Collection<? extends E> collection) {
177    Object[] elements = collection.toArray();
178    switch (elements.length) {
179      case 0:
180        return of();
181      case 1:
182        @SuppressWarnings("unchecked") // collection had only Es in it
183        ImmutableList<E> list = new SingletonImmutableList<E>((E) elements[0]);
184        return list;
185      default:
186        return new RegularImmutableList<E>(ImmutableList.<E>nullCheckedList(elements));
187    }
188  }
189
190  // Factory method that skips the null checks.  Used only when the elements
191  // are guaranteed to be null.
192  static <E> ImmutableList<E> unsafeDelegateList(List<? extends E> list) {
193    switch (list.size()) {
194      case 0:
195        return of();
196      case 1:
197        return new SingletonImmutableList<E>(list.iterator().next());
198      default:
199        @SuppressWarnings("unchecked")
200        List<E> castedList = (List<E>) list;
201        return new RegularImmutableList<E>(castedList);
202    }
203  }
204
205  static <E> ImmutableList<E> backedBy(E[] elements) {
206    return unsafeDelegateList(Arrays.asList(elements));
207  }
208
209  private static <E> List<E> nullCheckedList(Object... array) {
210    for (int i = 0, len = array.length; i < len; i++) {
211      if (array[i] == null) {
212        throw new NullPointerException("at index " + i);
213      }
214    }
215    @SuppressWarnings("unchecked")
216    E[] castedArray = (E[]) array;
217    return Arrays.asList(castedArray);
218  }
219
220  public int indexOf(@Nullable Object object) {
221    return delegate.indexOf(object);
222  }
223
224  public int lastIndexOf(@Nullable Object object) {
225    return delegate.lastIndexOf(object);
226  }
227
228  public final boolean addAll(int index, Collection<? extends E> newElements) {
229    throw new UnsupportedOperationException();
230  }
231
232  public final E set(int index, E element) {
233    throw new UnsupportedOperationException();
234  }
235
236  public final void add(int index, E element) {
237    throw new UnsupportedOperationException();
238  }
239
240  public final E remove(int index) {
241    throw new UnsupportedOperationException();
242  }
243
244  public E get(int index) {
245    return delegate.get(index);
246  }
247
248  public ImmutableList<E> subList(int fromIndex, int toIndex) {
249    return unsafeDelegateList(delegate.subList(fromIndex, toIndex));
250  }
251
252  public ListIterator<E> listIterator() {
253    return delegate.listIterator();
254  }
255
256  public ListIterator<E> listIterator(int index) {
257    return delegate.listIterator(index);
258  }
259
260  @Override public ImmutableList<E> asList() {
261    return this;
262  }
263
264  public ImmutableList<E> reverse(){
265    List<E> list = Lists.newArrayList(this);
266    Collections.reverse(list);
267    return unsafeDelegateList(list);
268  }
269
270  @Override public Object[] toArray() {
271    // Note that ArrayList.toArray() doesn't work here because it returns E[]
272    // instead of Object[].
273    return delegate.toArray(new Object[size()]);
274  }
275
276  @Override public boolean equals(Object obj) {
277    return delegate.equals(obj);
278  }
279
280  @Override public int hashCode() {
281    return delegate.hashCode();
282  }
283
284  public static <E> Builder<E> builder() {
285    return new Builder<E>();
286  }
287
288  public static final class Builder<E> extends ImmutableCollection.Builder<E> {
289    private final ArrayList<E> contents = Lists.newArrayList();
290
291    public Builder() {}
292
293    @Override public Builder<E> add(E element) {
294      contents.add(checkNotNull(element));
295      return this;
296    }
297
298    @Override public Builder<E> addAll(Iterable<? extends E> elements) {
299      super.addAll(elements);
300      return this;
301    }
302
303    @Override public Builder<E> add(E... elements) {
304      checkNotNull(elements);  // for GWT
305      super.add(elements);
306      return this;
307    }
308
309    @Override public Builder<E> addAll(Iterator<? extends E> elements) {
310      super.addAll(elements);
311      return this;
312    }
313
314    @Override public ImmutableList<E> build() {
315      return copyOf(contents);
316    }
317  }
318}
319