package com.bumptech.glide.load.model;
import android.support.v4.util.Pools.Pool;
import com.bumptech.glide.util.Synthetic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Maintains an ordered put of {@link ModelLoader}s and the model and data types they handle in
* order from highest priority to lowest.
*/
public class ModelLoaderRegistry {
private final MultiModelLoaderFactory multiModelLoaderFactory;
private final ModelLoaderCache cache = new ModelLoaderCache();
public ModelLoaderRegistry(Pool<List<Exception>> exceptionListPool) {
this(new MultiModelLoaderFactory(exceptionListPool));
}
// Visible for testing.
ModelLoaderRegistry(MultiModelLoaderFactory multiModelLoaderFactory) {
this.multiModelLoaderFactory = multiModelLoaderFactory;
}
public synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
public synchronized <Model, Data> void prepend(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
multiModelLoaderFactory.prepend(modelClass, dataClass, factory);
cache.clear();
}
public synchronized <Model, Data> void remove(Class<Model> modelClass, Class<Data> dataClass) {
tearDown(multiModelLoaderFactory.remove(modelClass, dataClass));
cache.clear();
}
public synchronized <Model, Data> void replace(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
tearDown(multiModelLoaderFactory.replace(modelClass, dataClass, factory));
cache.clear();
}
private <Model, Data> void tearDown(List<ModelLoaderFactory<Model, Data>> factories) {
for (ModelLoaderFactory<?, ?> factory : factories) {
factory.teardown();
}
}
public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass,
Class<Data> dataClass) {
return multiModelLoaderFactory.build(modelClass, dataClass);
}
public synchronized List<Class<?>> getDataClasses(Class<?> modelClass) {
return multiModelLoaderFactory.getDataClasses(modelClass);
}
private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}
@SuppressWarnings("unchecked")
private static <A> Class<A> getClass(A model) {
return (Class<A>) model.getClass();
}
private static class ModelLoaderCache {
private final Map<Class<?>, Entry<?>> cachedModelLoaders = new HashMap<>();
@Synthetic
ModelLoaderCache() { }
public void clear() {
cachedModelLoaders.clear();
}
public <Model> void put(Class<Model> modelClass, List<ModelLoader<Model, ?>> loaders) {
Entry<?> previous = cachedModelLoaders.put(modelClass, new Entry<>(loaders));
if (previous != null) {
throw new IllegalStateException("Already cached loaders for model: " + modelClass);
}
}
@SuppressWarnings("unchecked")
public <Model> List<ModelLoader<Model, ?>> get(Class<Model> modelClass) {
Entry<Model> entry = (Entry<Model>) cachedModelLoaders.get(modelClass);
return entry == null ? null : entry.loaders;
}
private static class Entry<Model> {
@Synthetic final List<ModelLoader<Model, ?>> loaders;
public Entry(List<ModelLoader<Model, ?>> loaders) {
this.loaders = loaders;
}
}
}
}