1 /* 2 * Copyright (C) 2013 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 android.util; 18 19 import android.annotation.Nullable; 20 21 import java.lang.reflect.Array; 22 import java.util.Collection; 23 import java.util.Iterator; 24 import java.util.Map; 25 import java.util.NoSuchElementException; 26 import java.util.Objects; 27 import java.util.Set; 28 29 /** 30 * Helper for writing standard Java collection interfaces to a data 31 * structure like {@link ArrayMap}. 32 * @hide 33 */ 34 @android.ravenwood.annotation.RavenwoodKeepWholeClass 35 abstract class MapCollections<K, V> { 36 EntrySet mEntrySet; 37 KeySet mKeySet; 38 ValuesCollection mValues; 39 40 final class ArrayIterator<T> implements Iterator<T> { 41 final int mOffset; 42 int mSize; 43 int mIndex; 44 boolean mCanRemove = false; 45 ArrayIterator(int offset)46 ArrayIterator(int offset) { 47 mOffset = offset; 48 mSize = colGetSize(); 49 } 50 51 @Override hasNext()52 public boolean hasNext() { 53 return mIndex < mSize; 54 } 55 56 @Override next()57 public T next() { 58 if (!hasNext()) throw new NoSuchElementException(); 59 Object res = colGetEntry(mIndex, mOffset); 60 mIndex++; 61 mCanRemove = true; 62 return (T)res; 63 } 64 65 @Override remove()66 public void remove() { 67 if (!mCanRemove) { 68 throw new IllegalStateException(); 69 } 70 mIndex--; 71 mSize--; 72 mCanRemove = false; 73 colRemoveAt(mIndex); 74 } 75 } 76 77 final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> { 78 int mEnd; 79 int mIndex; 80 boolean mEntryValid = false; 81 MapIterator()82 MapIterator() { 83 mEnd = colGetSize() - 1; 84 mIndex = -1; 85 } 86 87 @Override hasNext()88 public boolean hasNext() { 89 return mIndex < mEnd; 90 } 91 92 @Override next()93 public Map.Entry<K, V> next() { 94 if (!hasNext()) throw new NoSuchElementException(); 95 mIndex++; 96 mEntryValid = true; 97 return this; 98 } 99 100 @Override remove()101 public void remove() { 102 if (!mEntryValid) { 103 throw new IllegalStateException(); 104 } 105 colRemoveAt(mIndex); 106 mIndex--; 107 mEnd--; 108 mEntryValid = false; 109 } 110 111 @Override getKey()112 public K getKey() { 113 if (!mEntryValid) { 114 throw new IllegalStateException( 115 "This container does not support retaining Map.Entry objects"); 116 } 117 return (K)colGetEntry(mIndex, 0); 118 } 119 120 @Override getValue()121 public V getValue() { 122 if (!mEntryValid) { 123 throw new IllegalStateException( 124 "This container does not support retaining Map.Entry objects"); 125 } 126 return (V)colGetEntry(mIndex, 1); 127 } 128 129 @Override setValue(V object)130 public V setValue(V object) { 131 if (!mEntryValid) { 132 throw new IllegalStateException( 133 "This container does not support retaining Map.Entry objects"); 134 } 135 return colSetValue(mIndex, object); 136 } 137 138 @Override equals(Object o)139 public final boolean equals(Object o) { 140 if (!mEntryValid) { 141 throw new IllegalStateException( 142 "This container does not support retaining Map.Entry objects"); 143 } 144 if (!(o instanceof Map.Entry)) { 145 return false; 146 } 147 Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; 148 return Objects.equals(e.getKey(), colGetEntry(mIndex, 0)) 149 && Objects.equals(e.getValue(), colGetEntry(mIndex, 1)); 150 } 151 152 @Override hashCode()153 public final int hashCode() { 154 if (!mEntryValid) { 155 throw new IllegalStateException( 156 "This container does not support retaining Map.Entry objects"); 157 } 158 final Object key = colGetEntry(mIndex, 0); 159 final Object value = colGetEntry(mIndex, 1); 160 return (key == null ? 0 : key.hashCode()) ^ 161 (value == null ? 0 : value.hashCode()); 162 } 163 164 @Override toString()165 public final String toString() { 166 return getKey() + "=" + getValue(); 167 } 168 } 169 170 final class EntrySet implements Set<Map.Entry<K, V>> { 171 @Override add(Map.Entry<K, V> object)172 public boolean add(Map.Entry<K, V> object) { 173 throw new UnsupportedOperationException(); 174 } 175 176 @Override addAll(Collection<? extends Map.Entry<K, V>> collection)177 public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) { 178 int oldSize = colGetSize(); 179 for (Map.Entry<K, V> entry : collection) { 180 colPut(entry.getKey(), entry.getValue()); 181 } 182 return oldSize != colGetSize(); 183 } 184 185 @Override clear()186 public void clear() { 187 colClear(); 188 } 189 190 @Override contains(Object o)191 public boolean contains(Object o) { 192 if (!(o instanceof Map.Entry)) 193 return false; 194 Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; 195 int index = colIndexOfKey(e.getKey()); 196 if (index < 0) { 197 return false; 198 } 199 Object foundVal = colGetEntry(index, 1); 200 return Objects.equals(foundVal, e.getValue()); 201 } 202 203 @Override containsAll(Collection<?> collection)204 public boolean containsAll(Collection<?> collection) { 205 Iterator<?> it = collection.iterator(); 206 while (it.hasNext()) { 207 if (!contains(it.next())) { 208 return false; 209 } 210 } 211 return true; 212 } 213 214 @Override isEmpty()215 public boolean isEmpty() { 216 return colGetSize() == 0; 217 } 218 219 @Override iterator()220 public Iterator<Map.Entry<K, V>> iterator() { 221 return new MapIterator(); 222 } 223 224 @Override remove(Object object)225 public boolean remove(Object object) { 226 throw new UnsupportedOperationException(); 227 } 228 229 @Override removeAll(Collection<?> collection)230 public boolean removeAll(Collection<?> collection) { 231 throw new UnsupportedOperationException(); 232 } 233 234 @Override retainAll(Collection<?> collection)235 public boolean retainAll(Collection<?> collection) { 236 throw new UnsupportedOperationException(); 237 } 238 239 @Override size()240 public int size() { 241 return colGetSize(); 242 } 243 244 @Override toArray()245 public Object[] toArray() { 246 throw new UnsupportedOperationException(); 247 } 248 249 @Override toArray(T[] array)250 public <T> T[] toArray(T[] array) { 251 throw new UnsupportedOperationException(); 252 } 253 254 @Override equals(@ullable Object object)255 public boolean equals(@Nullable Object object) { 256 return equalsSetHelper(this, object); 257 } 258 259 @Override hashCode()260 public int hashCode() { 261 int result = 0; 262 for (int i=colGetSize()-1; i>=0; i--) { 263 final Object key = colGetEntry(i, 0); 264 final Object value = colGetEntry(i, 1); 265 result += ( (key == null ? 0 : key.hashCode()) ^ 266 (value == null ? 0 : value.hashCode()) ); 267 } 268 return result; 269 } 270 }; 271 272 final class KeySet implements Set<K> { 273 274 @Override add(K object)275 public boolean add(K object) { 276 throw new UnsupportedOperationException(); 277 } 278 279 @Override addAll(Collection<? extends K> collection)280 public boolean addAll(Collection<? extends K> collection) { 281 throw new UnsupportedOperationException(); 282 } 283 284 @Override clear()285 public void clear() { 286 colClear(); 287 } 288 289 @Override contains(Object object)290 public boolean contains(Object object) { 291 return colIndexOfKey(object) >= 0; 292 } 293 294 @Override containsAll(Collection<?> collection)295 public boolean containsAll(Collection<?> collection) { 296 return containsAllHelper(colGetMap(), collection); 297 } 298 299 @Override isEmpty()300 public boolean isEmpty() { 301 return colGetSize() == 0; 302 } 303 304 @Override iterator()305 public Iterator<K> iterator() { 306 return new ArrayIterator<K>(0); 307 } 308 309 @Override remove(Object object)310 public boolean remove(Object object) { 311 int index = colIndexOfKey(object); 312 if (index >= 0) { 313 colRemoveAt(index); 314 return true; 315 } 316 return false; 317 } 318 319 @Override removeAll(Collection<?> collection)320 public boolean removeAll(Collection<?> collection) { 321 return removeAllHelper(colGetMap(), collection); 322 } 323 324 @Override retainAll(Collection<?> collection)325 public boolean retainAll(Collection<?> collection) { 326 return retainAllHelper(colGetMap(), collection); 327 } 328 329 @Override size()330 public int size() { 331 return colGetSize(); 332 } 333 334 @Override toArray()335 public Object[] toArray() { 336 return toArrayHelper(0); 337 } 338 339 @Override toArray(T[] array)340 public <T> T[] toArray(T[] array) { 341 return toArrayHelper(array, 0); 342 } 343 344 @Override equals(@ullable Object object)345 public boolean equals(@Nullable Object object) { 346 return equalsSetHelper(this, object); 347 } 348 349 @Override hashCode()350 public int hashCode() { 351 int result = 0; 352 for (int i=colGetSize()-1; i>=0; i--) { 353 Object obj = colGetEntry(i, 0); 354 result += obj == null ? 0 : obj.hashCode(); 355 } 356 return result; 357 } 358 359 @Override toString()360 public String toString() { 361 return toStringHelper(0, this, "KeySet"); 362 } 363 } 364 365 final class ValuesCollection implements Collection<V> { 366 367 @Override add(V object)368 public boolean add(V object) { 369 throw new UnsupportedOperationException(); 370 } 371 372 @Override addAll(Collection<? extends V> collection)373 public boolean addAll(Collection<? extends V> collection) { 374 throw new UnsupportedOperationException(); 375 } 376 377 @Override clear()378 public void clear() { 379 colClear(); 380 } 381 382 @Override contains(Object object)383 public boolean contains(Object object) { 384 return colIndexOfValue(object) >= 0; 385 } 386 387 @Override containsAll(Collection<?> collection)388 public boolean containsAll(Collection<?> collection) { 389 Iterator<?> it = collection.iterator(); 390 while (it.hasNext()) { 391 if (!contains(it.next())) { 392 return false; 393 } 394 } 395 return true; 396 } 397 398 @Override isEmpty()399 public boolean isEmpty() { 400 return colGetSize() == 0; 401 } 402 403 @Override iterator()404 public Iterator<V> iterator() { 405 return new ArrayIterator<V>(1); 406 } 407 408 @Override remove(Object object)409 public boolean remove(Object object) { 410 int index = colIndexOfValue(object); 411 if (index >= 0) { 412 colRemoveAt(index); 413 return true; 414 } 415 return false; 416 } 417 418 @Override removeAll(Collection<?> collection)419 public boolean removeAll(Collection<?> collection) { 420 int N = colGetSize(); 421 boolean changed = false; 422 for (int i=0; i<N; i++) { 423 Object cur = colGetEntry(i, 1); 424 if (collection.contains(cur)) { 425 colRemoveAt(i); 426 i--; 427 N--; 428 changed = true; 429 } 430 } 431 return changed; 432 } 433 434 @Override retainAll(Collection<?> collection)435 public boolean retainAll(Collection<?> collection) { 436 int N = colGetSize(); 437 boolean changed = false; 438 for (int i=0; i<N; i++) { 439 Object cur = colGetEntry(i, 1); 440 if (!collection.contains(cur)) { 441 colRemoveAt(i); 442 i--; 443 N--; 444 changed = true; 445 } 446 } 447 return changed; 448 } 449 450 @Override size()451 public int size() { 452 return colGetSize(); 453 } 454 455 @Override toArray()456 public Object[] toArray() { 457 return toArrayHelper(1); 458 } 459 460 @Override toArray(T[] array)461 public <T> T[] toArray(T[] array) { 462 return toArrayHelper(array, 1); 463 } 464 465 @Override toString()466 public String toString() { 467 return toStringHelper(1, this, "ValuesCollection"); 468 } 469 } 470 containsAllHelper(Map<K, V> map, Collection<?> collection)471 public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) { 472 Iterator<?> it = collection.iterator(); 473 while (it.hasNext()) { 474 if (!map.containsKey(it.next())) { 475 return false; 476 } 477 } 478 return true; 479 } 480 removeAllHelper(Map<K, V> map, Collection<?> collection)481 public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) { 482 int oldSize = map.size(); 483 Iterator<?> it = collection.iterator(); 484 while (it.hasNext()) { 485 map.remove(it.next()); 486 } 487 return oldSize != map.size(); 488 } 489 retainAllHelper(Map<K, V> map, Collection<?> collection)490 public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) { 491 int oldSize = map.size(); 492 Iterator<K> it = map.keySet().iterator(); 493 while (it.hasNext()) { 494 if (!collection.contains(it.next())) { 495 it.remove(); 496 } 497 } 498 return oldSize != map.size(); 499 } 500 toArrayHelper(int offset)501 public Object[] toArrayHelper(int offset) { 502 final int N = colGetSize(); 503 Object[] result = new Object[N]; 504 for (int i=0; i<N; i++) { 505 result[i] = colGetEntry(i, offset); 506 } 507 return result; 508 } 509 toArrayHelper(T[] array, int offset)510 public <T> T[] toArrayHelper(T[] array, int offset) { 511 final int N = colGetSize(); 512 if (array.length < N) { 513 @SuppressWarnings("unchecked") T[] newArray 514 = (T[]) Array.newInstance(array.getClass().getComponentType(), N); 515 array = newArray; 516 } 517 for (int i=0; i<N; i++) { 518 array[i] = (T)colGetEntry(i, offset); 519 } 520 if (array.length > N) { 521 array[N] = null; 522 } 523 return array; 524 } 525 toStringHelper(int offset, Object thing, String thingName)526 private String toStringHelper(int offset, Object thing, String thingName) { 527 int size = colGetSize(); 528 if (size == 0) { 529 return "[]"; 530 } 531 532 StringBuilder buffer = new StringBuilder(size * 14); 533 buffer.append('['); 534 for (int i = 0; i < size; i++) { 535 if (i > 0) { 536 buffer.append(", "); 537 } 538 Object entry = colGetEntry(i, offset); 539 if (entry != thing) { 540 buffer.append(entry); 541 } else { 542 buffer.append("(this ").append(thingName).append(")"); 543 } 544 } 545 buffer.append(']'); 546 return buffer.toString(); 547 } 548 equalsSetHelper(Set<T> set, Object object)549 public static <T> boolean equalsSetHelper(Set<T> set, Object object) { 550 if (set == object) { 551 return true; 552 } 553 if (object instanceof Set) { 554 Set<?> s = (Set<?>) object; 555 556 try { 557 return set.size() == s.size() && set.containsAll(s); 558 } catch (NullPointerException ignored) { 559 return false; 560 } catch (ClassCastException ignored) { 561 return false; 562 } 563 } 564 return false; 565 } 566 getEntrySet()567 public Set<Map.Entry<K, V>> getEntrySet() { 568 if (mEntrySet == null) { 569 mEntrySet = new EntrySet(); 570 } 571 return mEntrySet; 572 } 573 getKeySet()574 public Set<K> getKeySet() { 575 if (mKeySet == null) { 576 mKeySet = new KeySet(); 577 } 578 return mKeySet; 579 } 580 getValues()581 public Collection<V> getValues() { 582 if (mValues == null) { 583 mValues = new ValuesCollection(); 584 } 585 return mValues; 586 } 587 colGetSize()588 protected abstract int colGetSize(); colGetEntry(int index, int offset)589 protected abstract Object colGetEntry(int index, int offset); colIndexOfKey(Object key)590 protected abstract int colIndexOfKey(Object key); colIndexOfValue(Object key)591 protected abstract int colIndexOfValue(Object key); colGetMap()592 protected abstract Map<K, V> colGetMap(); colPut(K key, V value)593 protected abstract void colPut(K key, V value); colSetValue(int index, V value)594 protected abstract V colSetValue(int index, V value); colRemoveAt(int index)595 protected abstract void colRemoveAt(int index); colClear()596 protected abstract void colClear(); 597 } 598