001 /*
002 * Copyright 2010-2016 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.utils;
018
019 import org.jetbrains.annotations.NotNull;
020
021 import java.lang.reflect.Array;
022 import java.util.*;
023
024 /**
025 * A List which is optimised for the sizes of 0 and 1,
026 * in which cases it would not allocate array at all.
027 *
028 * This class was copied from com.intellij.util.SmartList.
029 */
030 @SuppressWarnings("unchecked")
031 public class SmartList<E> extends AbstractList<E> implements RandomAccess {
032 private int mySize;
033 private Object myElem; // null if mySize==0, (E)elem if mySize==1, Object[] if mySize>=2
034
035 public SmartList() { }
036
037 public SmartList(E element) {
038 add(element);
039 }
040
041 public SmartList(@NotNull Collection<? extends E> elements) {
042 int size = elements.size();
043 if (size == 1) {
044 E element = elements instanceof List ? (E)((List)elements).get(0) : elements.iterator().next();
045 add(element);
046 }
047 else if (size > 0) {
048 mySize = size;
049 myElem = elements.toArray(new Object[size]);
050 }
051 }
052
053 public SmartList(@NotNull E... elements) {
054 if (elements.length == 1) {
055 add(elements[0]);
056 }
057 else if (elements.length > 0) {
058 mySize = elements.length;
059 myElem = Arrays.copyOf(elements, mySize);
060 }
061 }
062
063 @Override
064 public E get(int index) {
065 if (index < 0 || index >= mySize) {
066 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize);
067 }
068 if (mySize == 1) {
069 return (E)myElem;
070 }
071 return (E)((Object[])myElem)[index];
072 }
073
074 @Override
075 public boolean add(E e) {
076 if (mySize == 0) {
077 myElem = e;
078 }
079 else if (mySize == 1) {
080 Object[] array = new Object[2];
081 array[0] = myElem;
082 array[1] = e;
083 myElem = array;
084 }
085 else {
086 Object[] array = (Object[])myElem;
087 int oldCapacity = array.length;
088 if (mySize >= oldCapacity) {
089 // have to resize
090 int newCapacity = oldCapacity * 3 / 2 + 1;
091 int minCapacity = mySize + 1;
092 if (newCapacity < minCapacity) {
093 newCapacity = minCapacity;
094 }
095 Object[] oldArray = array;
096 myElem = array = new Object[newCapacity];
097 System.arraycopy(oldArray, 0, array, 0, oldCapacity);
098 }
099 array[mySize] = e;
100 }
101
102 mySize++;
103 modCount++;
104 return true;
105 }
106
107 @Override
108 public void add(int index, E e) {
109 if (index < 0 || index > mySize) {
110 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize);
111 }
112
113 if (mySize == 0) {
114 myElem = e;
115 }
116 else if (mySize == 1 && index == 0) {
117 Object[] array = new Object[2];
118 array[0] = e;
119 array[1] = myElem;
120 myElem = array;
121 }
122 else {
123 Object[] array = new Object[mySize + 1];
124 if (mySize == 1) {
125 array[0] = myElem; // index == 1
126 }
127 else {
128 Object[] oldArray = (Object[])myElem;
129 System.arraycopy(oldArray, 0, array, 0, index);
130 System.arraycopy(oldArray, index, array, index + 1, mySize - index);
131 }
132 array[index] = e;
133 myElem = array;
134 }
135
136 mySize++;
137 modCount++;
138 }
139
140 @Override
141 public int size() {
142 return mySize;
143 }
144
145 @Override
146 public void clear() {
147 myElem = null;
148 mySize = 0;
149 modCount++;
150 }
151
152 @Override
153 public E set(int index, E element) {
154 if (index < 0 || index >= mySize) {
155 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize);
156 }
157
158 E oldValue;
159 if (mySize == 1) {
160 oldValue = (E)myElem;
161 myElem = element;
162 }
163 else {
164 Object[] array = (Object[])myElem;
165 oldValue = (E)array[index];
166 array[index] = element;
167 }
168 return oldValue;
169 }
170
171 @Override
172 public E remove(int index) {
173 if (index < 0 || index >= mySize) {
174 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + mySize);
175 }
176
177 E oldValue;
178 if (mySize == 1) {
179 oldValue = (E)myElem;
180 myElem = null;
181 }
182 else {
183 Object[] array = (Object[])myElem;
184 oldValue = (E)array[index];
185
186 if (mySize == 2) {
187 myElem = array[1 - index];
188 }
189 else {
190 int numMoved = mySize - index - 1;
191 if (numMoved > 0) {
192 System.arraycopy(array, index + 1, array, index, numMoved);
193 }
194 array[mySize - 1] = null;
195 }
196 }
197 mySize--;
198 modCount++;
199 return oldValue;
200 }
201
202 private static class EmptyIterator<T> implements Iterator<T> {
203 private static final EmptyIterator INSTANCE = new EmptyIterator();
204 public static <T> EmptyIterator<T> getInstance() {
205 //noinspection unchecked
206 return INSTANCE;
207 }
208 @Override
209 public boolean hasNext() {
210 return false;
211 }
212
213 @Override
214 public T next() {
215 throw new NoSuchElementException();
216 }
217
218 @Override
219 public void remove() {
220 throw new IllegalStateException();
221 }
222 }
223
224
225 @NotNull
226 @Override
227 public Iterator<E> iterator() {
228 if (mySize == 0) {
229 return EmptyIterator.getInstance();
230 }
231 if (mySize == 1) {
232 return new SingletonIterator();
233 }
234 return super.iterator();
235 }
236
237
238 private abstract static class SingletonIteratorBase<T> implements Iterator<T> {
239 private boolean myVisited;
240
241 @Override
242 public final boolean hasNext() {
243 return !myVisited;
244 }
245
246 @Override
247 public final T next() {
248 if (myVisited) {
249 throw new NoSuchElementException();
250 }
251 myVisited = true;
252 checkCoModification();
253 return getElement();
254 }
255
256 protected abstract void checkCoModification();
257
258 protected abstract T getElement();
259 }
260
261 private class SingletonIterator extends SingletonIteratorBase<E> {
262 private final int myInitialModCount;
263
264 public SingletonIterator() {
265 myInitialModCount = modCount;
266 }
267
268 @Override
269 protected E getElement() {
270 return (E)myElem;
271 }
272
273 @Override
274 protected void checkCoModification() {
275 if (modCount != myInitialModCount) {
276 throw new ConcurrentModificationException("ModCount: " + modCount + "; expected: " + myInitialModCount);
277 }
278 }
279
280 @Override
281 public void remove() {
282 checkCoModification();
283 clear();
284 }
285 }
286
287 public void sort(Comparator<? super E> comparator) {
288 if (mySize >= 2) {
289 Arrays.sort((E[])myElem, 0, mySize, comparator);
290 }
291 }
292
293 public int getModificationCount() {
294 return modCount;
295 }
296
297 @NotNull
298 @Override
299 public <T> T[] toArray(@NotNull T[] a) {
300 int aLength = a.length;
301 if (mySize == 1) {
302 if (aLength != 0) {
303 a[0] = (T)myElem;
304 }
305 else {
306 T[] r = (T[])Array.newInstance(a.getClass().getComponentType(), 1);
307 r[0] = (T)myElem;
308 return r;
309 }
310 }
311 else if (aLength < mySize) {
312 return (T[])Arrays.copyOf((E[])myElem, mySize, a.getClass());
313 }
314 else if (mySize != 0) {
315 //noinspection SuspiciousSystemArraycopy
316 System.arraycopy(myElem, 0, a, 0, mySize);
317 }
318
319 if (aLength > mySize) {
320 a[mySize] = null;
321 }
322 return a;
323 }
324
325 /**
326 * Trims the capacity of this list to be the
327 * list's current size. An application can use this operation to minimize
328 * the storage of a list instance.
329 */
330 public void trimToSize() {
331 if (mySize < 2) return;
332 Object[] array = (Object[])myElem;
333 int oldCapacity = array.length;
334 if (mySize < oldCapacity) {
335 modCount++;
336 myElem = Arrays.copyOf(array, mySize);
337 }
338 }
339 }