1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.paging
18 
19 import androidx.arch.core.util.Function
20 import java.util.IdentityHashMap
21 
22 @Suppress("DEPRECATION")
23 internal class WrapperItemKeyedDataSource<K : Any, A : Any, B : Any>(
24     private val source: ItemKeyedDataSource<K, A>,
25     private val listFunction: Function<List<A>, List<B>>
26 ) : ItemKeyedDataSource<K, B>() {
27 
28     private val keyMap = IdentityHashMap<B, K>()
29 
addInvalidatedCallbacknull30     override fun addInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
31         source.addInvalidatedCallback(onInvalidatedCallback)
32     }
33 
removeInvalidatedCallbacknull34     override fun removeInvalidatedCallback(onInvalidatedCallback: InvalidatedCallback) {
35         source.removeInvalidatedCallback(onInvalidatedCallback)
36     }
37 
invalidatenull38     override fun invalidate() {
39         source.invalidate()
40     }
41 
42     override val isInvalid
43         get() = source.isInvalid
44 
convertWithStashedKeysnull45     fun convertWithStashedKeys(source: List<A>): List<B> {
46         val dest = convert(listFunction, source)
47         synchronized(keyMap) {
48             // synchronize on keyMap, since multiple loads may occur simultaneously.
49             // Note: manually sync avoids locking per-item (e.g. Collections.synchronizedMap)
50             for (i in dest.indices) {
51                 keyMap[dest[i]] = this.source.getKey(source[i])
52             }
53         }
54         return dest
55     }
56 
loadInitialnull57     override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
58         source.loadInitial(
59             params,
60             object : LoadInitialCallback<A>() {
61                 override fun onResult(data: List<A>, position: Int, totalCount: Int) {
62                     callback.onResult(convertWithStashedKeys(data), position, totalCount)
63                 }
64 
65                 override fun onResult(data: List<A>) {
66                     callback.onResult(convertWithStashedKeys(data))
67                 }
68             }
69         )
70     }
71 
loadAfternull72     override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<B>) {
73         source.loadAfter(
74             params,
75             object : LoadCallback<A>() {
76                 override fun onResult(data: List<A>) {
77                     callback.onResult(convertWithStashedKeys(data))
78                 }
79             }
80         )
81     }
82 
loadBeforenull83     override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<B>) {
84         source.loadBefore(
85             params,
86             object : LoadCallback<A>() {
87                 override fun onResult(data: List<A>) {
88                     callback.onResult(convertWithStashedKeys(data))
89                 }
90             }
91         )
92     }
93 
getKeynull94     override fun getKey(item: B): K =
95         synchronized(keyMap) {
96             return keyMap[item]!!
97         }
98 }
99