1a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar/*
2a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * Copyright (C) 2014 The Android Open Source Project
3a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar *
4a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * you may not use this file except in compliance with the License.
6a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * You may obtain a copy of the License at
7a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar *
8a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar *
10a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * Unless required by applicable law or agreed to in writing, software
11a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * See the License for the specific language governing permissions and
14a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * limitations under the License.
15a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar */
16a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
17a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyarpackage android.support.v7.util;
18a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
19a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyarimport java.lang.reflect.Array;
2012833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyarimport java.util.Arrays;
217fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheevimport java.util.Collection;
227fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheevimport java.util.Comparator;
23a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
24a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar/**
25a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * A Sorted list implementation that can keep items in order and also notify for changes in the
26a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * list
27a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter
28a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * RecyclerView.Adapter}.
29a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * <p>
30a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses
31a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * binary search to retrieve items. If the sorting criteria of your items may change, make sure you
32a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * call appropriate methods while editing them to avoid data inconsistencies.
33a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * <p>
34a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar * You can control the order of items and change notifications via the {@link Callback} parameter.
35a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar */
36a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar@SuppressWarnings("unchecked")
37a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyarpublic class SortedList<T> {
38a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
39a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
40a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Used by {@link #indexOf(Object)} when he item cannot be found in the list.
41a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
42a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public static final int INVALID_POSITION = -1;
43a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
44a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private static final int MIN_CAPACITY = 10;
45a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private static final int CAPACITY_GROWTH = MIN_CAPACITY;
46a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private static final int INSERTION = 1;
47a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private static final int DELETION = 1 << 1;
48a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private static final int LOOKUP = 1 << 2;
49a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    T[] mData;
50a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
51a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
527fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * A copy of the previous list contents used during the merge phase of addAll.
537fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
547fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private T[] mOldData;
557fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int mOldDataStart;
567fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int mOldDataSize;
577fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
587fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
597fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * The size of the valid portion of mData during the merge phase of addAll.
607fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
617fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int mMergedSize;
627fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
637fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
647fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
65a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * The callback instance that controls the behavior of the SortedList and get notified when
66a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * changes happen.
67a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
68a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private Callback mCallback;
69a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
70a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private BatchedCallback mBatchedCallback;
71a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
72a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private int mSize;
73a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private final Class<T> mTClass;
74a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
75a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
76a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Creates a new SortedList of type T.
77a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
78a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param klass    The class of the contents of the SortedList.
79a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param callback The callback that controls the behavior of SortedList.
80a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
81a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public SortedList(Class<T> klass, Callback<T> callback) {
82a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        this(klass, callback, MIN_CAPACITY);
83a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
84a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
85a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
86a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Creates a new SortedList of type T.
87a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
88a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param klass           The class of the contents of the SortedList.
89a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param callback        The callback that controls the behavior of SortedList.
90a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param initialCapacity The initial capacity to hold items.
91a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
92a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public SortedList(Class<T> klass, Callback<T> callback, int initialCapacity) {
93a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mTClass = klass;
94a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mData = (T[]) Array.newInstance(klass, initialCapacity);
95a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mCallback = callback;
96a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mSize = 0;
97a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
98a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
99a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
100a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * The number of items in the list.
101a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
102a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return The number of items in the list.
103a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
104a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public int size() {
105a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return mSize;
106a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
107a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
108a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
109a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Adds the given item to the list. If this is a new item, SortedList calls
110a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#onInserted(int, int)}.
111a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
112a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the item already exists in the list and its sorting criteria is not changed, it is
113a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * replaced with the existing Item. SortedList uses
114a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item
115a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should
116a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the
117a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * reference to the old item and puts the new item into the backing array even if
118a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#areContentsTheSame(Object, Object)} returns false.
119a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
120a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the sorting criteria of the item is changed, SortedList won't be able to find
121a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * its duplicate in the list which will result in having a duplicate of the Item in the list.
122a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If you need to update sorting criteria of an item that already exists in the list,
123a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * use {@link #updateItemAt(int, Object)}. You can find the index of the item using
124a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link #indexOf(Object)} before you update the object.
125a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
126a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param item The item to be added into the list.
1277fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
128a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return The index of the newly added item.
129a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see {@link Callback#compare(Object, Object)}
130a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see {@link Callback#areItemsTheSame(Object, Object)}
131a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see {@link Callback#areContentsTheSame(Object, Object)}}
132a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
133a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public int add(T item) {
1347fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
135a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return add(item, true);
136a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
137a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
138a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
1397fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * Adds the given items to the list. Equivalent to calling {@link SortedList#add} in a loop,
1407fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * except the callback events may be in a different order/granularity since addAll can batch
1417fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * them for better performance.
1427fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * <p>
1437fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * If allowed, may modify the input array and even take the ownership over it in order
1447fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * to avoid extra memory allocation during sorting and deduplication.
1457fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * </p>
1467fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @param items Array of items to be added into the list.
1477fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @param mayModifyInput If true, SortedList is allowed to modify the input.
148d805095048f6be52cddbd572ee343c4639ba8187Alan Viverette     * @see {@link SortedList#addAll(Object[] items)}.
1497fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
1507fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    public void addAll(T[] items, boolean mayModifyInput) {
1517fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
1527fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (items.length == 0) {
1537fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            return;
1547fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
1557fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (mayModifyInput) {
1567fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            addAllInternal(items);
1577fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        } else {
1587fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            T[] copy = (T[]) Array.newInstance(mTClass, items.length);
1597fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            System.arraycopy(items, 0, copy, 0, items.length);
1607fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            addAllInternal(copy);
1617fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
1627fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1637fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
1647fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1657fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
1667fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * Adds the given items to the list. Does not modify the input.
1677fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
1687fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @see {@link SortedList#addAll(T[] items, boolean mayModifyInput)}
1697fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
1707fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @param items Array of items to be added into the list.
1717fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
1727fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    public void addAll(T... items) {
1737fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        addAll(items, false);
1747fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
1757fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1767fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
1777fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * Adds the given items to the list. Does not modify the input.
1787fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
1797fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @see {@link SortedList#addAll(T[] items, boolean mayModifyInput)}
1807fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
1817fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @param items Collection of items to be added into the list.
1827fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
1837fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    public void addAll(Collection<T> items) {
1847fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        T[] copy = (T[]) Array.newInstance(mTClass, items.size());
1857fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        addAll(items.toArray(copy), true);
1867fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
1877fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1887fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private void addAllInternal(T[] newItems) {
1897fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        final boolean forceBatchedUpdates = !(mCallback instanceof BatchedCallback);
1907fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (forceBatchedUpdates) {
1917fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            beginBatchedUpdates();
1927fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
1937fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1947fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mOldData = mData;
1957fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mOldDataStart = 0;
1967fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mOldDataSize = mSize;
1977fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
1987fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        Arrays.sort(newItems, mCallback);  // Arrays.sort is stable.
1997fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2007fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        final int newSize = deduplicate(newItems);
2017fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (mSize == 0) {
2027fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            mData = newItems;
2037fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            mSize = newSize;
2047fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            mMergedSize = newSize;
2057fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            mCallback.onInserted(0, newSize);
2067fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        } else {
2077fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            merge(newItems, newSize);
2087fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
2097fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2107fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mOldData = null;
2117fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2127fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (forceBatchedUpdates) {
2137fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            endBatchedUpdates();
2147fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
2157fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
2167fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2177fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
2187fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * Remove duplicate items, leaving only the last item from each group of "same" items.
2197fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * Move the remaining items to the beginning of the array.
2207fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
2217fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * @return Number of deduplicated items at the beginning of the array.
2227fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
2237fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int deduplicate(T[] items) {
2247fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (items.length == 0) {
2257fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            throw new IllegalArgumentException("Input array must be non-empty");
2267fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
2277fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2287fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        // Keep track of the range of equal items at the end of the output.
2297fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        // Start with the range containing just the first item.
2307fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        int rangeStart = 0;
2317fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        int rangeEnd = 1;
2327fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2337fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        for (int i = 1; i < items.length; ++i) {
2347fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            T currentItem = items[i];
2357fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2367fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            int compare = mCallback.compare(items[rangeStart], currentItem);
2377fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (compare > 0) {
2387fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                throw new IllegalArgumentException("Input must be sorted in ascending order.");
2397fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
2407fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2417fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (compare == 0) {
2427fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // The range of equal items continues, update it.
2437fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                final int sameItemPos = findSameItem(currentItem, items, rangeStart, rangeEnd);
2447fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                if (sameItemPos != INVALID_POSITION) {
2457fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    // Replace the duplicate item.
2467fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    items[sameItemPos] = currentItem;
2477fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                } else {
2487fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    // Expand the range.
2497fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    if (rangeEnd != i) {  // Avoid redundant copy.
2507fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                        items[rangeEnd] = currentItem;
2517fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    }
2527fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    rangeEnd++;
2537fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                }
2547fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            } else {
2557fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // The range has ended. Reset it to contain just the current item.
2567fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                if (rangeEnd != i) {  // Avoid redundant copy.
2577fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    items[rangeEnd] = currentItem;
2587fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                }
2597fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                rangeStart = rangeEnd++;
2607fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
2617fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
2627fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        return rangeEnd;
2637fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
2647fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2657fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2667fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int findSameItem(T item, T[] items, int from, int to) {
2677fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        for (int pos = from; pos < to; pos++) {
2687fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (mCallback.areItemsTheSame(items[pos], item)) {
2697fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                return pos;
2707fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
2717fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
2727fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        return INVALID_POSITION;
2737fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
2747fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2757fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
2767fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     * This method assumes that newItems are sorted and deduplicated.
2777fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     */
2787fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private void merge(T[] newData, int newDataSize) {
2797fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        final int mergedCapacity = mSize + newDataSize + CAPACITY_GROWTH;
2807fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mData = (T[]) Array.newInstance(mTClass, mergedCapacity);
2817fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        mMergedSize = 0;
2827fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2837fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        int newDataStart = 0;
2847fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        while (mOldDataStart < mOldDataSize || newDataStart < newDataSize) {
2857fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (mOldDataStart == mOldDataSize) {
2867fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // No more old items, copy the remaining new items.
2877fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                int itemCount = newDataSize - newDataStart;
2887fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                System.arraycopy(newData, newDataStart, mData, mMergedSize, itemCount);
2897fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mMergedSize += itemCount;
2907fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mSize += itemCount;
2917fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mCallback.onInserted(mMergedSize - itemCount, itemCount);
2927fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                break;
2937fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
2947fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
2957fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (newDataStart == newDataSize) {
2967fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // No more new items, copy the remaining old items.
2977fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                int itemCount = mOldDataSize - mOldDataStart;
2987fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                System.arraycopy(mOldData, mOldDataStart, mData, mMergedSize, itemCount);
2997fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mMergedSize += itemCount;
3007fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                break;
3017fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
3027fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
3037fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            T oldItem = mOldData[mOldDataStart];
3047fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            T newItem = newData[newDataStart];
3057fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            int compare = mCallback.compare(oldItem, newItem);
3067fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (compare > 0) {
3077fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // New item is lower, output it.
3087fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mData[mMergedSize++] = newItem;
3097fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mSize++;
3107fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                newDataStart++;
3117fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mCallback.onInserted(mMergedSize - 1, 1);
3127fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            } else if (compare == 0 && mCallback.areItemsTheSame(oldItem, newItem)) {
3137fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // Items are the same. Output the new item, but consume both.
3147fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mData[mMergedSize++] = newItem;
3157fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                newDataStart++;
3167fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mOldDataStart++;
3177fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                if (!mCallback.areContentsTheSame(oldItem, newItem)) {
3187fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                    mCallback.onChanged(mMergedSize - 1, 1);
3197fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                }
3207fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            } else {
3217fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // Old item is lower than or equal to (but not the same as the new). Output it.
3227fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                // New item with the same sort order will be inserted later.
3237fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mData[mMergedSize++] = oldItem;
3247fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                mOldDataStart++;
3257fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
3267fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
3277fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
3287fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
3297fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private void throwIfMerging() {
3307fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (mOldData != null) {
3317fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            throw new IllegalStateException("Cannot call this method from within addAll");
3327fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
3337fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    }
3347fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev
3357fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    /**
336a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Batches adapter updates that happen between calling this method until calling
337a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop
338a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * and they are placed into consecutive indices, SortedList calls
339a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event
340a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * cannot be merged with the previous event, the previous event is dispatched
341a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * to the callback instantly.
342a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
343a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}
344a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * which will dispatch any deferred data change event to the current callback.
345a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
346a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * A sample implementation may look like this:
347a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <pre>
348a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     mSortedList.beginBatchedUpdates();
349a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     try {
350a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *         mSortedList.add(item1)
351a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *         mSortedList.add(item2)
352a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *         mSortedList.remove(item3)
353a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *         ...
354a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     } finally {
355a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *         mSortedList.endBatchedUpdates();
356a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     }
357a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * </pre>
358a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
359a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Instead of using this method to batch calls, you can use a Callback that extends
360a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link BatchedCallback}. In that case, you must make sure that you are manually calling
361a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes.
362a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Failing to do so may create data inconsistencies with the Callback.
363a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
364a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the current Callback in an instance of {@link BatchedCallback}, calling this method
365a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * has no effect.
366a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
367a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public void beginBatchedUpdates() {
3687fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
369a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (mCallback instanceof BatchedCallback) {
370a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            return;
371a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
372a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (mBatchedCallback == null) {
373a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mBatchedCallback = new BatchedCallback(mCallback);
374a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
375a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mCallback = mBatchedCallback;
376a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
377a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
378a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
379a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Ends the update transaction and dispatches any remaining event to the callback.
380a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
381a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public void endBatchedUpdates() {
3827fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
383a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (mCallback instanceof BatchedCallback) {
384a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            ((BatchedCallback) mCallback).dispatchLastEvent();
385a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
386a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (mCallback == mBatchedCallback) {
387a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback = mBatchedCallback.mWrappedCallback;
388a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
389a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
390a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
391a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private int add(T item, boolean notify) {
3927fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        int index = findIndexOf(item, mData, 0, mSize, INSERTION);
393a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index == INVALID_POSITION) {
394a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            index = 0;
395a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        } else if (index < mSize) {
396a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            T existing = mData[index];
397a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mCallback.areItemsTheSame(existing, item)) {
398a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                if (mCallback.areContentsTheSame(existing, item)) {
399a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    //no change but still replace the item
400a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mData[index] = item;
401a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    return index;
402a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                } else {
403a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mData[index] = item;
404a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mCallback.onChanged(index, 1);
405a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    return index;
406a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                }
407a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
408a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
409a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        addToData(index, item);
410a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (notify) {
411a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback.onInserted(index, 1);
412a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
413a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return index;
414a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
415a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
416a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
417a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}.
418a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
419a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param item The item to be removed from the list.
4207fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
421a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return True if item is removed, false if item cannot be found in the list.
422a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
423a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public boolean remove(T item) {
4247fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
425a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return remove(item, true);
426a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
427a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
428a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
429a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}.
430a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
431a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param index The index of the item to be removed.
4327fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
433a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return The removed item.
434a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
435a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public T removeItemAt(int index) {
4367fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
437a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        T item = get(index);
438a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        removeItemAtIndex(index, true);
439a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return item;
440a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
441a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
442a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private boolean remove(T item, boolean notify) {
4437fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        int index = findIndexOf(item, mData, 0, mSize, DELETION);
444a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index == INVALID_POSITION) {
445a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            return false;
446a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
447a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        removeItemAtIndex(index, notify);
448a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return true;
449a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
450a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
451a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private void removeItemAtIndex(int index, boolean notify) {
452a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        System.arraycopy(mData, index + 1, mData, index, mSize - index - 1);
453a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mSize--;
454a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mData[mSize] = null;
455a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (notify) {
456a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback.onRemoved(index, 1);
457a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
458a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
459a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
460a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
461a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or
462a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#onMoved(int, int)} if necessary.
463a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
464a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * You can use this method if you need to change an existing Item such that its position in the
465a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * list may change.
466a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
467a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the new object is a different object (<code>get(index) != item</code>) and
468a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList
469a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls
470a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * {@link Callback#onChanged(int, int)}.
471a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
472a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the new position of the item is different than the provided <code>index</code>,
473a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * SortedList
474a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * calls {@link Callback#onMoved(int, int)}.
475a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
476a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param index The index of the item to replace
477a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param item  The item to replace the item at the given Index.
478a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see #add(Object)
479a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
480a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public void updateItemAt(int index, T item) {
4817fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
482a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        final T existing = get(index);
483a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        // assume changed if the same object is given back
484a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item);
485a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (existing != item) {
486a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            // different items, we can use comparison and may avoid lookup
487a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            final int cmp = mCallback.compare(existing, item);
488a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (cmp == 0) {
489a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mData[index] = item;
490a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                if (contentsChanged) {
491a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mCallback.onChanged(index, 1);
492a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                }
493a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return;
494a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
495a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
496a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (contentsChanged) {
497a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback.onChanged(index, 1);
498a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
499a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        // TODO this done in 1 pass to avoid shifting twice.
500a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        removeItemAtIndex(index, false);
501a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        int newIndex = add(item, false);
502a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index != newIndex) {
503a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback.onMoved(index, newIndex);
504a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
505a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
506a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
507a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
508a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * This method can be used to recalculate the position of the item at the given index, without
509a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * triggering an {@link Callback#onChanged(int, int)} callback.
510a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
511a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If you are editing objects in the list such that their position in the list may change but
512a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * you don't want to trigger an onChange animation, you can use this method to re-position it.
513a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)}
514a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * without
515a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * calling {@link Callback#onChanged(int, int)}.
516a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
517a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * A sample usage may look like:
518a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
519a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <pre>
520a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     final int position = mSortedList.indexOf(item);
521a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     item.incrementPriority(); // assume items are sorted by priority
522a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *     mSortedList.recalculatePositionOfItemAt(position);
523a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * </pre>
524a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * In the example above, because the sorting criteria of the item has been changed,
525a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * mSortedList.indexOf(item) will not be able to find the item. This is why the code above
526a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * first
527a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * gets the position before editing the item, edits it and informs the SortedList that item
528a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * should be repositioned.
529a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
530a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param index The current index of the Item whose position should be re-calculated.
531a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see #updateItemAt(int, Object)
532a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @see #add(Object)
533a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
534a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public void recalculatePositionOfItemAt(int index) {
5357fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
536a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        // TODO can be improved
537a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        final T item = get(index);
538a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        removeItemAtIndex(index, false);
539a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        int newIndex = add(item, false);
540a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index != newIndex) {
541a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mCallback.onMoved(index, newIndex);
542a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
543a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
544a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
545a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
546a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Returns the item at the given index.
547a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
548a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param index The index of the item to retrieve.
5497fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
550a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return The item at the given index.
551a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the
552a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *                                             size of the list.
553a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
554a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public T get(int index) throws IndexOutOfBoundsException {
555a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index >= mSize || index < 0) {
556a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is "
557a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    + mSize);
558a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
5597fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (mOldData != null) {
5607fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            // The call is made from a callback during addAll execution. The data is split
5617fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            // between mData and mOldData.
5627fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (index >= mMergedSize) {
5637fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                return mOldData[index - mMergedSize + mOldDataStart];
5647fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
5657fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
566a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return mData[index];
567a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
568a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
569a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
570a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * Returns the position of the provided item.
571a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     *
572a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @param item The item to query for position.
5737fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev     *
574a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the
575a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * list.
576a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
577a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public int indexOf(T item) {
5787fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        if (mOldData != null) {
5797fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            int index = findIndexOf(item, mData, 0, mMergedSize, LOOKUP);
5807fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (index != INVALID_POSITION) {
5817fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                return index;
5827fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
5837fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            index = findIndexOf(item, mOldData, mOldDataStart, mOldDataSize, LOOKUP);
5847fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            if (index != INVALID_POSITION) {
5857fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev                return index - mOldDataStart + mMergedSize;
5867fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            }
5877fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev            return INVALID_POSITION;
5887fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        }
5897fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        return findIndexOf(item, mData, 0, mSize, LOOKUP);
590a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
591a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
5927fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    private int findIndexOf(T item, T[] mData, int left, int right, int reason) {
593a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        while (left < right) {
594a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            final int middle = (left + right) / 2;
595a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            T myItem = mData[middle];
596a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            final int cmp = mCallback.compare(myItem, item);
597a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (cmp < 0) {
598a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                left = middle + 1;
599a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            } else if (cmp == 0) {
600a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                if (mCallback.areItemsTheSame(myItem, item)) {
601a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    return middle;
602a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                } else {
603a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    int exact = linearEqualitySearch(item, middle, left, right);
604a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    if (reason == INSERTION) {
605a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                        return exact == INVALID_POSITION ? middle : exact;
606a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    } else {
607a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                        return exact;
608a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    }
609a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                }
610a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            } else {
611a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                right = middle;
612a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
613a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
614a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return reason == INSERTION ? left : INVALID_POSITION;
615a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
616a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
617a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private int linearEqualitySearch(T item, int middle, int left, int right) {
618a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        // go left
619a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        for (int next = middle - 1; next >= left; next--) {
620a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            T nextItem = mData[next];
621a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            int cmp = mCallback.compare(nextItem, item);
622a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (cmp != 0) {
623a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                break;
624a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
625a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mCallback.areItemsTheSame(nextItem, item)) {
626a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return next;
627a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
628a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
629a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        for (int next = middle + 1; next < right; next++) {
630a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            T nextItem = mData[next];
631a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            int cmp = mCallback.compare(nextItem, item);
632a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (cmp != 0) {
633a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                break;
634a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
635a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mCallback.areItemsTheSame(nextItem, item)) {
636a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return next;
637a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
638a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
639a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        return INVALID_POSITION;
640a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
641a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
642a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    private void addToData(int index, T item) {
643a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (index > mSize) {
644a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            throw new IndexOutOfBoundsException(
645a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    "cannot add item to " + index + " because size is " + mSize);
646a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
647a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        if (mSize == mData.length) {
648a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            // we are at the limit enlarge
649a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH);
650a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            System.arraycopy(mData, 0, newData, 0, index);
651a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            newData[index] = item;
652a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            System.arraycopy(mData, index, newData, index + 1, mSize - index);
653a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mData = newData;
654a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        } else {
655a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            // just shift, we fit
656a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            System.arraycopy(mData, index, mData, index + 1, mSize - index);
657a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mData[index] = item;
658a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
659a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        mSize++;
660a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
661a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
662a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
66312833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar     * Removes all items from the SortedList.
66412833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar     */
66512833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar    public void clear() {
6667fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev        throwIfMerging();
66712833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        if (mSize == 0) {
66812833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar            return;
66912833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        }
67012833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        final int prevSize = mSize;
67112833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        Arrays.fill(mData, 0, prevSize, null);
67212833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        mSize = 0;
67312833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar        mCallback.onRemoved(0, prevSize);
67412833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar    }
67512833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar
67612833d36969f1540dd5fc6fe32a25fa8b52a8e0bYigit Boyar    /**
677a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * The class that controls the behavior of the {@link SortedList}.
678a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
679a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * It defines how items should be sorted and how duplicates should be handled.
680a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
681a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * SortedList calls the callback methods on this class to notify changes about the underlying
682a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * data.
683a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
6847fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev    public static abstract class Callback<T2> implements Comparator<T2> {
685a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
686a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
687a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and
688a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * return how they should be ordered.
689a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
690a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param o1 The first object to compare.
691a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param o2 The second object to compare.
6927fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev         *
693a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @return a negative integer, zero, or a positive integer as the
694a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * first argument is less than, equal to, or greater than the
695a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * second.
696a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
697a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public int compare(T2 o1, T2 o2);
698a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
699a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
700a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList when an item is inserted at the given position.
701a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
702a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param position The position of the new item.
703a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param count    The number of items that have been added.
704a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
705a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public void onInserted(int position, int count);
706a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
707a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
708a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList when an item is removed from the given position.
709a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
710a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param position The position of the item which has been removed.
711a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param count    The number of items which have been removed.
712a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
713a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public void onRemoved(int position, int count);
714a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
715a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
716a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList when an item changes its position in the list.
717a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
718a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param fromPosition The previous position of the item before the move.
719a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param toPosition   The new position of the item.
720a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
721a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public void onMoved(int fromPosition, int toPosition);
722a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
723a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
724a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList when the item at the given position is updated.
725a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
726a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param position The position of the item which has been updated.
727a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param count    The number of items which has changed.
728a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
729a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public void onChanged(int position, int count);
730a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
731a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
732a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList when it wants to check whether two items have the same data
733a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * or not. SortedList uses this information to decide whether it should call
734a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * {@link #onChanged(int, int)} or not.
735a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * <p>
736a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * SortedList uses this method to check equality instead of {@link Object#equals(Object)}
737a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * so
738a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * that you can change its behavior depending on your UI.
739a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * <p>
740a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter
741a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * RecyclerView.Adapter}, you should
742a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * return whether the items' visual representations are the same or not.
743a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
744a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param oldItem The previous representation of the object.
745a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param newItem The new object that replaces the previous one.
7467fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev         *
747a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @return True if the contents of the items are the same or false if they are different.
748a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
749a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);
750a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
751a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
752a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Called by the SortedList to decide whether two object represent the same Item or not.
753a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * <p>
754a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * For example, if your items have unique ids, this method should check their equality.
755a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
756a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param item1 The first item to check.
757a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param item2 The second item to check.
7587fa39157f88d13bc417dab8db7c13e342a14cba0Vladislav Kaznacheev         *
759a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @return True if the two items represent the same object or false if they are different.
760a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
761a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        abstract public boolean areItemsTheSame(T2 item1, T2 item2);
762a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
763a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
764a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    /**
765a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * A callback implementation that can batch notify events dispatched by the SortedList.
766a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
767a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * This class can be useful if you want to do multiple operations on a SortedList but don't
768a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * want to dispatch each event one by one, which may result in a performance issue.
769a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
770a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * For example, if you are going to add multiple items to a SortedList, BatchedCallback call
771a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * convert individual <code>onInserted(index, 1)</code> calls into one
772a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
773a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * can help RecyclerView resolve changes much more easily.
774a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * <p>
775a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
776a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * dispatches them as soon as such case is detected. After your edits on the SortedList is
777a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
778a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     * all changes to the Callback.
779a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar     */
780a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    public static class BatchedCallback<T2> extends Callback<T2> {
781a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
782a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        private final Callback<T2> mWrappedCallback;
783a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        static final int TYPE_NONE = 0;
784a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        static final int TYPE_ADD = 1;
785a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        static final int TYPE_REMOVE = 2;
786a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        static final int TYPE_CHANGE = 3;
787a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        static final int TYPE_MOVE = 4;
788a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
789a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        int mLastEventType = TYPE_NONE;
790a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        int mLastEventPosition = -1;
791a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        int mLastEventCount = -1;
792a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
793a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
794a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * Creates a new BatchedCallback that wraps the provided Callback.
795a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *
796a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * @param wrappedCallback The Callback which should received the data change callbacks.
797a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *                        Other method calls (e.g. {@link #compare(Object, Object)} from
798a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         *                        the SortedList are directly forwarded to this Callback.
799a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
800a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public BatchedCallback(Callback<T2> wrappedCallback) {
801a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mWrappedCallback = wrappedCallback;
802a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
803a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
804a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
805a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public int compare(T2 o1, T2 o2) {
806a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            return mWrappedCallback.compare(o1, o2);
807a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
808a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
809a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
810a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public void onInserted(int position, int count) {
811a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mLastEventType == TYPE_ADD && position >= mLastEventPosition
812a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    && position <= mLastEventPosition + mLastEventCount) {
813a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mLastEventCount += count;
814a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mLastEventPosition = Math.min(position, mLastEventPosition);
815a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return;
816a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
817a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            dispatchLastEvent();
818a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventPosition = position;
819a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventCount = count;
820a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventType = TYPE_ADD;
821a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
822a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
823a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
824a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public void onRemoved(int position, int count) {
825a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) {
826a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mLastEventCount += count;
827a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return;
828a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
829a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            dispatchLastEvent();
830a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventPosition = position;
831a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventCount = count;
832a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventType = TYPE_REMOVE;
833a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
834a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
835a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
836a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public void onMoved(int fromPosition, int toPosition) {
837a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            dispatchLastEvent();//moves are not merged
838a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mWrappedCallback.onMoved(fromPosition, toPosition);
839a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
840a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
841a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
842a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public void onChanged(int position, int count) {
843a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mLastEventType == TYPE_CHANGE &&
844a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    !(position > mLastEventPosition + mLastEventCount
845a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                            || position + count < mLastEventPosition)) {
846a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                // take potential overlap into account
847a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                int previousEnd = mLastEventPosition + mLastEventCount;
848a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mLastEventPosition = Math.min(position, mLastEventPosition);
849a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;
850a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return;
851a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
852a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            dispatchLastEvent();
853a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventPosition = position;
854a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventCount = count;
855a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventType = TYPE_CHANGE;
856a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
857a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
858a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
859a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
860a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            return mWrappedCallback.areContentsTheSame(oldItem, newItem);
861a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
862a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
863a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        @Override
864a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public boolean areItemsTheSame(T2 item1, T2 item2) {
865a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            return mWrappedCallback.areItemsTheSame(item1, item2);
866a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
867a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
868a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar
869a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        /**
870a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * This method dispatches any pending event notifications to the wrapped Callback.
871a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         * You <b>must</b> always call this method after you are done with editing the SortedList.
872a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar         */
873a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        public void dispatchLastEvent() {
874a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            if (mLastEventType == TYPE_NONE) {
875a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                return;
876a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
877a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            switch (mLastEventType) {
878a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                case TYPE_ADD:
879a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount);
880a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    break;
881a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                case TYPE_REMOVE:
882a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount);
883a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    break;
884a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                case TYPE_CHANGE:
885a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount);
886a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar                    break;
887a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            }
888a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar            mLastEventType = TYPE_NONE;
889a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar        }
890a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar    }
891a3d5bb01bc01733999d84c452a27012c57ab369cYigit Boyar}
892