1 /*
2  * Copyright (C) 2018 The Android Open Source Project
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 
17 package com.android.launcher3.util;
18 
19 import java.util.Arrays;
20 import java.util.Iterator;
21 import java.util.StringTokenizer;
22 
23 /**
24  * Copy of the platform hidden implementation of android.util.IntArray.
25  * Implements a growing array of int primitives.
26  */
27 public class IntArray implements Cloneable, Iterable<Integer> {
28     private static final int MIN_CAPACITY_INCREMENT = 12;
29 
30     private static final int[] EMPTY_INT = new int[0];
31 
32     /* package private */ int[] mValues;
33     /* package private */ int mSize;
34 
IntArray(int[] array, int size)35     private  IntArray(int[] array, int size) {
36         mValues = array;
37         mSize = size;
38     }
39 
40     /**
41      * Creates an empty IntArray with the default initial capacity.
42      */
IntArray()43     public IntArray() {
44         this(10);
45     }
46 
47     /**
48      * Creates an empty IntArray with the specified initial capacity.
49      */
IntArray(int initialCapacity)50     public IntArray(int initialCapacity) {
51         if (initialCapacity == 0) {
52             mValues = EMPTY_INT;
53         } else {
54             mValues = new int[initialCapacity];
55         }
56         mSize = 0;
57     }
58 
59     /**
60      * Creates an IntArray wrapping the given primitive int array.
61      */
wrap(int... array)62     public static IntArray wrap(int... array) {
63         return new IntArray(array, array.length);
64     }
65 
66     /**
67      * Appends the specified value to the end of this array.
68      */
add(int value)69     public void add(int value) {
70         add(mSize, value);
71     }
72 
73     /**
74      * Inserts a value at the specified position in this array. If the specified index is equal to
75      * the length of the array, the value is added at the end.
76      *
77      * @throws IndexOutOfBoundsException when index &lt; 0 || index &gt; size()
78      */
add(int index, int value)79     public void add(int index, int value) {
80         ensureCapacity(1);
81         int rightSegment = mSize - index;
82         mSize++;
83         checkBounds(mSize, index);
84 
85         if (rightSegment != 0) {
86             // Move by 1 all values from the right of 'index'
87             System.arraycopy(mValues, index, mValues, index + 1, rightSegment);
88         }
89 
90         mValues[index] = value;
91     }
92 
93     /**
94      * Adds the values in the specified array to this array.
95      */
addAll(IntArray values)96     public void addAll(IntArray values) {
97         final int count = values.mSize;
98         ensureCapacity(count);
99 
100         System.arraycopy(values.mValues, 0, mValues, mSize, count);
101         mSize += count;
102     }
103 
104     /**
105      * Sets the array to be same as {@param other}
106      */
copyFrom(IntArray other)107     public void copyFrom(IntArray other) {
108         clear();
109         addAll(other);
110     }
111 
112     /**
113      * Ensures capacity to append at least <code>count</code> values.
114      */
ensureCapacity(int count)115     private void ensureCapacity(int count) {
116         final int currentSize = mSize;
117         final int minCapacity = currentSize + count;
118         if (minCapacity >= mValues.length) {
119             final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
120                     MIN_CAPACITY_INCREMENT : currentSize >> 1);
121             final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
122             final int[] newValues = new int[newCapacity];
123             System.arraycopy(mValues, 0, newValues, 0, currentSize);
124             mValues = newValues;
125         }
126     }
127 
128     /**
129      * Removes all values from this array.
130      */
clear()131     public void clear() {
132         mSize = 0;
133     }
134 
135     @Override
clone()136     public IntArray clone() {
137         return wrap(toArray());
138     }
139 
140     @Override
equals(Object obj)141     public boolean equals(Object obj) {
142         if (obj == this) {
143             return true;
144         }
145         if (obj instanceof IntArray) {
146             IntArray arr = (IntArray) obj;
147             if (mSize == arr.mSize) {
148                 for (int i = 0; i < mSize; i++) {
149                     if (arr.mValues[i] != mValues[i]) {
150                         return false;
151                     }
152                 }
153                 return true;
154             }
155         }
156         return false;
157     }
158 
159     /**
160      * Returns the value at the specified position in this array.
161      */
get(int index)162     public int get(int index) {
163         checkBounds(mSize, index);
164         return mValues[index];
165     }
166 
167     /**
168      * Sets the value at the specified position in this array.
169      */
set(int index, int value)170     public void set(int index, int value) {
171         checkBounds(mSize, index);
172         mValues[index] = value;
173     }
174 
175     /**
176      * Returns the index of the first occurrence of the specified value in this
177      * array, or -1 if this array does not contain the value.
178      */
indexOf(int value)179     public int indexOf(int value) {
180         final int n = mSize;
181         for (int i = 0; i < n; i++) {
182             if (mValues[i] == value) {
183                 return i;
184             }
185         }
186         return -1;
187     }
188 
contains(int value)189     public boolean contains(int value) {
190         return indexOf(value) >= 0;
191     }
192 
isEmpty()193     public boolean isEmpty() {
194         return mSize == 0;
195     }
196 
197     /**
198      * Removes the value at the specified index from this array.
199      */
removeIndex(int index)200     public void removeIndex(int index) {
201         checkBounds(mSize, index);
202         System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
203         mSize--;
204     }
205 
206     /**
207      * Removes the values if it exists
208      */
removeValue(int value)209     public void removeValue(int value) {
210         int index = indexOf(value);
211         if (index >= 0) {
212             removeIndex(index);
213         }
214     }
215 
216     /**
217      * Removes the values if it exists
218      */
removeAllValues(IntArray values)219     public void removeAllValues(IntArray values) {
220         for (int i = 0; i < values.mSize; i++) {
221             removeValue(values.mValues[i]);
222         }
223     }
224 
225     /**
226      * Returns the number of values in this array.
227      */
size()228     public int size() {
229         return mSize;
230     }
231 
232     /**
233      * Returns a new array with the contents of this IntArray.
234      */
toArray()235     public int[] toArray() {
236         return mSize == 0 ? EMPTY_INT : Arrays.copyOf(mValues, mSize);
237     }
238 
239     /**
240      * Returns a comma separate list of all values.
241      */
toConcatString()242     public String toConcatString() {
243         StringBuilder b = new StringBuilder();
244         for (int i = 0; i < mSize ; i++) {
245             if (i > 0) {
246                 b.append(", ");
247             }
248             b.append(mValues[i]);
249         }
250         return b.toString();
251     }
252 
253     @Override
toString()254     public String toString() {
255         return "IntArray [" + toConcatString() + "]";
256     }
257 
fromConcatString(String concatString)258     public static IntArray fromConcatString(String concatString) {
259         StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
260         int[] array = new int[tokenizer.countTokens()];
261         int count = 0;
262         while (tokenizer.hasMoreTokens()) {
263             array[count] = Integer.parseInt(tokenizer.nextToken().trim());
264             count++;
265         }
266         return new IntArray(array, array.length);
267     }
268 
269     /**
270      * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
271      *
272      * @param len length of the array. Must be non-negative
273      * @param index the index to check
274      * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
275      */
checkBounds(int len, int index)276     private static void checkBounds(int len, int index) {
277         if (index < 0 || len <= index) {
278             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
279         }
280     }
281 
282     @Override
iterator()283     public Iterator<Integer> iterator() {
284         return new ValueIterator();
285     }
286 
287     @Thunk
288     class ValueIterator implements Iterator<Integer> {
289 
290         private int mNextIndex = 0;
291 
292         @Override
hasNext()293         public boolean hasNext() {
294             return mNextIndex < size();
295         }
296 
297         @Override
next()298         public Integer next() {
299             return get(mNextIndex++);
300         }
301 
302         @Override
remove()303         public void remove() {
304             removeIndex(--mNextIndex);
305         }
306     }
307 }