1/* 2 * Copyright 2018 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 androidx.paging; 18 19import androidx.annotation.NonNull; 20import androidx.arch.core.util.Function; 21 22import java.util.IdentityHashMap; 23import java.util.List; 24 25class WrapperItemKeyedDataSource<K, A, B> extends ItemKeyedDataSource<K, B> { 26 private final ItemKeyedDataSource<K, A> mSource; 27 private final Function<List<A>, List<B>> mListFunction; 28 29 private final IdentityHashMap<B, K> mKeyMap = new IdentityHashMap<>(); 30 31 WrapperItemKeyedDataSource(ItemKeyedDataSource<K, A> source, 32 Function<List<A>, List<B>> listFunction) { 33 mSource = source; 34 mListFunction = listFunction; 35 } 36 37 @Override 38 public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) { 39 mSource.addInvalidatedCallback(onInvalidatedCallback); 40 } 41 42 @Override 43 public void removeInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) { 44 mSource.removeInvalidatedCallback(onInvalidatedCallback); 45 } 46 47 @Override 48 public void invalidate() { 49 mSource.invalidate(); 50 } 51 52 @Override 53 public boolean isInvalid() { 54 return mSource.isInvalid(); 55 } 56 57 private List<B> convertWithStashedKeys(List<A> source) { 58 List<B> dest = convert(mListFunction, source); 59 synchronized (mKeyMap) { 60 // synchronize on mKeyMap, since multiple loads may occur simultaneously. 61 // Note: manually sync avoids locking per-item (e.g. Collections.synchronizedMap) 62 for (int i = 0; i < dest.size(); i++) { 63 mKeyMap.put(dest.get(i), mSource.getKey(source.get(i))); 64 } 65 } 66 return dest; 67 } 68 69 @Override 70 public void loadInitial(@NonNull LoadInitialParams<K> params, 71 final @NonNull LoadInitialCallback<B> callback) { 72 mSource.loadInitial(params, new LoadInitialCallback<A>() { 73 @Override 74 public void onResult(@NonNull List<A> data, int position, int totalCount) { 75 callback.onResult(convertWithStashedKeys(data), position, totalCount); 76 } 77 78 @Override 79 public void onResult(@NonNull List<A> data) { 80 callback.onResult(convertWithStashedKeys(data)); 81 } 82 }); 83 } 84 85 @Override 86 public void loadAfter(@NonNull LoadParams<K> params, 87 final @NonNull LoadCallback<B> callback) { 88 mSource.loadAfter(params, new LoadCallback<A>() { 89 @Override 90 public void onResult(@NonNull List<A> data) { 91 callback.onResult(convertWithStashedKeys(data)); 92 } 93 }); 94 } 95 96 @Override 97 public void loadBefore(@NonNull LoadParams<K> params, 98 final @NonNull LoadCallback<B> callback) { 99 mSource.loadBefore(params, new LoadCallback<A>() { 100 @Override 101 public void onResult(@NonNull List<A> data) { 102 callback.onResult(convertWithStashedKeys(data)); 103 } 104 }); 105 } 106 107 @NonNull 108 @Override 109 public K getKey(@NonNull B item) { 110 synchronized (mKeyMap) { 111 return mKeyMap.get(item); 112 } 113 } 114} 115