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 < 0 || index > 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 }