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