• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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