19fc12334a7d14347cd6951d0653264b2597bd3a0Sam Juddpackage com.bumptech.glide.load.model;
20ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
30ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport android.content.Context;
4bcf4a0dae04a4ad14287eeb34069a97c96fe9bb1Sam Juddimport com.bumptech.glide.load.data.DataFetcher;
50ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
60ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport java.util.HashMap;
70ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport java.util.Map;
80ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
90ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd/**
100ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd * Maintain a map of model class to factory to retrieve a {@link ModelLoaderFactory} and/or a {@link ModelLoader}
110ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd * for a given model type.
120ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd */
130ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddpublic class GenericLoaderFactory {
140ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    private Map<Class, Map<Class, ModelLoaderFactory>> modelClassToResourceFactories =
150ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            new HashMap<Class, Map<Class, ModelLoaderFactory>>();
168e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    private Map<Class, Map<Class, ModelLoader>> cachedModelLoaders =
178e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            new HashMap<Class, Map<Class, ModelLoader>>();
180ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
19fce91ebaf2090f716c654954681fd818688a87adSam Judd    private static final ModelLoader NULL_MODEL_LOADER = new ModelLoader() {
20fce91ebaf2090f716c654954681fd818688a87adSam Judd        @Override
21bcf4a0dae04a4ad14287eeb34069a97c96fe9bb1Sam Judd        public DataFetcher getResourceFetcher(Object model, int width, int height) {
22fce91ebaf2090f716c654954681fd818688a87adSam Judd            throw new NoSuchMethodError("This should never be called!");
23fce91ebaf2090f716c654954681fd818688a87adSam Judd        }
24fce91ebaf2090f716c654954681fd818688a87adSam Judd
25fce91ebaf2090f716c654954681fd818688a87adSam Judd        @Override
26fce91ebaf2090f716c654954681fd818688a87adSam Judd        public String toString() {
27fce91ebaf2090f716c654954681fd818688a87adSam Judd            return "NULL_MODEL_LOADER";
28fce91ebaf2090f716c654954681fd818688a87adSam Judd        }
29fce91ebaf2090f716c654954681fd818688a87adSam Judd    };
30fce91ebaf2090f716c654954681fd818688a87adSam Judd
31031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    /**
32031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * Removes and returns the registered {@link ModelLoaderFactory} for the given model and resource classes. Returns
33031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * null if no such factory is registered. Clears all cached model loaders.
34031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     *
35031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param modelClass The model class.
36031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param resourceClass The resource class.
37031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <T> The type of the model the class.
38031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <Y> The type of the resource class.
39031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     */
40031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    @SuppressWarnings("unchecked")
41031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    public <T, Y> ModelLoaderFactory<T, Y> unregister(Class<T> modelClass, Class<Y> resourceClass) {
42031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        cachedModelLoaders.clear();
43031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd
44031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        ModelLoaderFactory<T, Y> result = null;
45031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
46031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        if (resourceToFactories != null) {
47031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd            result = resourceToFactories.remove(resourceClass);
48031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        }
49031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        return result;
50031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    }
51031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd
52031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    /**
53031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * Registers the given {@link ModelLoaderFactory} for the given model and resource classes and returns the previous
54031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * factory registered for the given model and resource classes or null if no such factory existed. Clears all cached
55031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * model loaders.
56031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     *
57031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param modelClass The model class.
58031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param resourceClass The resource class.
59031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param factory The factory to register.
60031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <T> The type of the model.
61031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <Y> The type of the resource.
62031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     */
630ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    @SuppressWarnings("unchecked")
640ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    public <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, Class<Y> resourceClass,
650ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            ModelLoaderFactory<T, Y> factory) {
66031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd        cachedModelLoaders.clear();
67031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd
680ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
690ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        if (resourceToFactories == null) {
700ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            resourceToFactories = new HashMap<Class, ModelLoaderFactory>();
710ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            modelClassToResourceFactories.put(modelClass, resourceToFactories);
720ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        }
730ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
740ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        ModelLoaderFactory<T, Y> previous = resourceToFactories.put(resourceClass, factory);
750ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
760ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        if (previous != null) {
770ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            // This factory may be being used by another model. We don't want to say it has been removed unless we
780ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            // know it has been removed for all models.
790ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            for (Map<Class, ModelLoaderFactory> currentResourceToFactories : modelClassToResourceFactories.values()) {
800ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                if (currentResourceToFactories.containsValue(previous)) {
810ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                    previous = null;
820ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                    break;
830ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                }
840ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            }
850ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        }
868e2641581399c6b767a1f5210689da8cc6fed6baSam Judd
870ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        return previous;
880ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    }
890ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
90031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd    /**
91031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * Returns a {@link ModelLoader} for the given model and resource classes by either returning a cached
92031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * {@link ModelLoader} or building a new a new {@link ModelLoader} using registered {@link ModelLoaderFactory}s.
93031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * Returns null if no {@link ModelLoaderFactory} is registered for the given classes.
94031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     *
95031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param modelClass The model class.
96031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param resourceClass The resource class.
97031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <T> The type of the model.
98031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     * @param <Y> The type of the resource.
99031fed2e364feacf89dfb904a3a0de98b050fdc4Sam Judd     */
1000ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    public <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass, Context context) {
1018e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
1028e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        if (result != null) {
103fce91ebaf2090f716c654954681fd818688a87adSam Judd            // We've already tried to create a model loader and can't with the currently registered set of factories, but
104fce91ebaf2090f716c654954681fd818688a87adSam Judd            // we can't use null to demonstrate that failure because model loaders that haven't been requested yet will
105fce91ebaf2090f716c654954681fd818688a87adSam Judd            // be null in the cache. To avoid this, we use a special signal model loader.
106fce91ebaf2090f716c654954681fd818688a87adSam Judd            if (NULL_MODEL_LOADER.equals(result)) {
107fce91ebaf2090f716c654954681fd818688a87adSam Judd                return null;
108fce91ebaf2090f716c654954681fd818688a87adSam Judd            } else {
109fce91ebaf2090f716c654954681fd818688a87adSam Judd                return result;
110fce91ebaf2090f716c654954681fd818688a87adSam Judd            }
1118e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        }
1128e2641581399c6b767a1f5210689da8cc6fed6baSam Judd
1130ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
1148e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        if (factory != null) {
1158e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            result = factory.build(context, this);
1168e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            cacheModelLoader(modelClass, resourceClass, result);
117fce91ebaf2090f716c654954681fd818688a87adSam Judd        } else {
118fce91ebaf2090f716c654954681fd818688a87adSam Judd            // We can't generate a model loader for the given arguments with the currently registered set of factories.
119fce91ebaf2090f716c654954681fd818688a87adSam Judd            cacheNullLoader(modelClass, resourceClass);
1208e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        }
1218e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        return result;
1228e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    }
1238e2641581399c6b767a1f5210689da8cc6fed6baSam Judd
124fce91ebaf2090f716c654954681fd818688a87adSam Judd    @SuppressWarnings("unchecked")
125fce91ebaf2090f716c654954681fd818688a87adSam Judd    private <T, Y> void cacheNullLoader(Class<T> modelClass, Class<Y> resourceClass) {
126fce91ebaf2090f716c654954681fd818688a87adSam Judd        cacheModelLoader(modelClass, resourceClass, NULL_MODEL_LOADER);
127fce91ebaf2090f716c654954681fd818688a87adSam Judd    }
128fce91ebaf2090f716c654954681fd818688a87adSam Judd
1298e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    private <T, Y> void cacheModelLoader(Class<T> modelClass, Class<Y> resourceClass, ModelLoader<T, Y> modelLoader) {
1308e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        Map<Class, ModelLoader> resourceToLoaders = cachedModelLoaders.get(modelClass);
1318e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        if (resourceToLoaders == null) {
1328e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            resourceToLoaders = new HashMap<Class, ModelLoader>();
1338e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            cachedModelLoaders.put(modelClass, resourceToLoaders);
1348e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        }
1358e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        resourceToLoaders.put(resourceClass, modelLoader);
1368e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    }
1378e2641581399c6b767a1f5210689da8cc6fed6baSam Judd
1388e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    @SuppressWarnings("unchecked")
1398e2641581399c6b767a1f5210689da8cc6fed6baSam Judd    private <T, Y> ModelLoader<T, Y> getCachedLoader(Class<T> modelClass, Class<Y> resourceClass) {
1408e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        Map<Class, ModelLoader> resourceToLoaders = cachedModelLoaders.get(modelClass);
1418e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        ModelLoader result = null;
1428e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        if (resourceToLoaders != null) {
1438e2641581399c6b767a1f5210689da8cc6fed6baSam Judd            result = resourceToLoaders.get(resourceClass);
1448e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        }
1458e2641581399c6b767a1f5210689da8cc6fed6baSam Judd
1468e2641581399c6b767a1f5210689da8cc6fed6baSam Judd        return result;
1470ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    }
1480ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
1490ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    @SuppressWarnings("unchecked")
15015df8d073a97cfa2b589b547535a5b055c71bfbbSam Judd    private <T, Y> ModelLoaderFactory<T, Y> getFactory(Class<T> modelClass, Class<Y> resourceClass) {
1510ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
1520ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        ModelLoaderFactory result = null;
1530ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        if (resourceToFactories != null) {
1540ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            result = resourceToFactories.get(resourceClass);
1550ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        }
1560ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
1570ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
1580ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        if (result == null) {
1590ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            for (Class registeredModelClass : modelClassToResourceFactories.keySet()) {
1600ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                // This accounts for model subclasses, our map only works for exact matches. We should however still
1610ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                // match a subclass of a model with a factory for a super class of that model if if there isn't a
1620ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                // factory for that particular subclass. Uris are a great example of when this happens, most uris
1630ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                // are actually subclasses for Uri, but we'd generally rather load them all with the same factory rather
1640ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                // than trying to register for each subclass individually.
1650ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                if (registeredModelClass.isAssignableFrom(modelClass)) {
1660ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                    Map<Class, ModelLoaderFactory> currentResourceToFactories =
1670ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                            modelClassToResourceFactories.get(registeredModelClass);
1680ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                    if (currentResourceToFactories != null) {
1690ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                        result = currentResourceToFactories.get(resourceClass);
1700ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                        if (result != null) {
1710ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                            break;
1720ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                        }
1730ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                    }
1740ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd                }
1750ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            }
1760ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        }
1770ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
1780ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        return result;
1790ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    }
1800ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd}
181