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 com.google.common.annotations.GwtCompatible;
20import com.google.common.base.Preconditions;
21
22import java.util.List;
23
24import javax.annotation.Nullable;
25
26/**
27 * Implementation of {@link ImmutableList} with one or more elements.
28 *
29 * @author Kevin Bourrillion
30 */
31@GwtCompatible(serializable = true, emulated = true)
32@SuppressWarnings("serial") // uses writeReplace(), not default serialization
33class RegularImmutableList<E> extends ImmutableList<E> {
34  private final transient int offset;
35  private final transient int size;
36  private final transient Object[] array;
37
38  RegularImmutableList(Object[] array, int offset, int size) {
39    this.offset = offset;
40    this.size = size;
41    this.array = array;
42  }
43
44  RegularImmutableList(Object[] array) {
45    this(array, 0, array.length);
46  }
47
48  @Override
49  public int size() {
50    return size;
51  }
52
53  @Override public boolean isEmpty() {
54    return false;
55  }
56
57  @Override boolean isPartialView() {
58    return offset != 0 || size != array.length;
59  }
60
61  @Override public boolean contains(@Nullable Object target) {
62    return indexOf(target) != -1;
63  }
64
65  // The fake cast to E is safe because the creation methods only allow E's
66  @SuppressWarnings("unchecked")
67  @Override public UnmodifiableIterator<E> iterator() {
68    return (UnmodifiableIterator<E>) Iterators.forArray(array, offset, size);
69  }
70
71  @Override public Object[] toArray() {
72    Object[] newArray = new Object[size()];
73    System.arraycopy(array, offset, newArray, 0, size);
74    return newArray;
75  }
76
77  @Override public <T> T[] toArray(T[] other) {
78    if (other.length < size) {
79      other = ObjectArrays.newArray(other, size);
80    } else if (other.length > size) {
81      other[size] = null;
82    }
83    System.arraycopy(array, offset, other, 0, size);
84    return other;
85  }
86
87  // The fake cast to E is safe because the creation methods only allow E's
88  @Override
89  @SuppressWarnings("unchecked")
90  public E get(int index) {
91    Preconditions.checkElementIndex(index, size);
92    return (E) array[index + offset];
93  }
94
95  @Override public int indexOf(@Nullable Object target) {
96    if (target != null) {
97      for (int i = offset; i < offset + size; i++) {
98        if (array[i].equals(target)) {
99          return i - offset;
100        }
101      }
102    }
103    return -1;
104  }
105
106  @Override public int lastIndexOf(@Nullable Object target) {
107    if (target != null) {
108      for (int i = offset + size - 1; i >= offset; i--) {
109        if (array[i].equals(target)) {
110          return i - offset;
111        }
112      }
113    }
114    return -1;
115  }
116
117  @Override public ImmutableList<E> subList(int fromIndex, int toIndex) {
118    Preconditions.checkPositionIndexes(fromIndex, toIndex, size);
119    return (fromIndex == toIndex)
120        ? ImmutableList.<E>of()
121        : new RegularImmutableList<E>(
122            array, offset + fromIndex, toIndex - fromIndex);
123  }
124
125  @Override public UnmodifiableListIterator<E> listIterator(final int start) {
126    return new AbstractIndexedListIterator<E>(size, start) {
127      // The fake cast to E is safe because the creation methods only allow E's
128      @SuppressWarnings("unchecked")
129      @Override protected E get(int index) {
130        return (E) array[index + offset];
131      }
132
133    };
134  }
135
136  @Override public boolean equals(@Nullable Object object) {
137    if (object == this) {
138      return true;
139    }
140    if (!(object instanceof List)) {
141      return false;
142    }
143
144    List<?> that = (List<?>) object;
145    if (this.size() != that.size()) {
146      return false;
147    }
148
149    int index = offset;
150    if (object instanceof RegularImmutableList) {
151      RegularImmutableList<?> other = (RegularImmutableList<?>) object;
152      for (int i = other.offset; i < other.offset + other.size; i++) {
153        if (!array[index++].equals(other.array[i])) {
154          return false;
155        }
156      }
157    } else {
158      for (Object element : that) {
159        if (!array[index++].equals(element)) {
160          return false;
161        }
162      }
163    }
164    return true;
165  }
166
167  @Override public int hashCode() {
168    // not caching hash code since it could change if the elements are mutable
169    // in a way that modifies their hash codes
170    int hashCode = 1;
171    for (int i = offset; i < offset + size; i++) {
172      hashCode = 31 * hashCode + array[i].hashCode();
173    }
174    return hashCode;
175  }
176
177  @Override public String toString() {
178    StringBuilder sb = Collections2.newStringBuilderForCollection(size())
179        .append('[').append(array[offset]);
180    for (int i = offset + 1; i < offset + size; i++) {
181      sb.append(", ").append(array[i]);
182    }
183    return sb.append(']').toString();
184  }
185}
186