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