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