package com.bumptech.glide;
import android.support.v4.util.Pools.Pool;
import com.bumptech.glide.load.Encoder;
import com.bumptech.glide.load.ImageHeaderParser;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.ResourceEncoder;
import com.bumptech.glide.load.data.DataRewinder;
import com.bumptech.glide.load.data.DataRewinderRegistry;
import com.bumptech.glide.load.engine.DecodePath;
import com.bumptech.glide.load.engine.LoadPath;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.ModelLoaderRegistry;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
import com.bumptech.glide.load.resource.transcode.TranscoderRegistry;
import com.bumptech.glide.provider.EncoderRegistry;
import com.bumptech.glide.provider.ImageHeaderParserRegistry;
import com.bumptech.glide.provider.LoadPathCache;
import com.bumptech.glide.provider.ModelToResourceClassCache;
import com.bumptech.glide.provider.ResourceDecoderRegistry;
import com.bumptech.glide.provider.ResourceEncoderRegistry;
import com.bumptech.glide.util.pool.FactoryPools;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Manages component registration.
*/
public class Registry {
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
private final ModelToResourceClassCache modelToResourceClassCache =
new ModelToResourceClassCache();
private final LoadPathCache loadPathCache = new LoadPathCache();
private final Pool<List<Exception>> exceptionListPool = FactoryPools.threadSafeList();
public Registry() {
this.modelLoaderRegistry = new ModelLoaderRegistry(exceptionListPool);
this.encoderRegistry = new EncoderRegistry();
this.decoderRegistry = new ResourceDecoderRegistry();
this.resourceEncoderRegistry = new ResourceEncoderRegistry();
this.dataRewinderRegistry = new DataRewinderRegistry();
this.transcoderRegistry = new TranscoderRegistry();
this.imageHeaderParserRegistry = new ImageHeaderParserRegistry();
}
public <Data> Registry register(Class<Data> dataClass, Encoder<Data> encoder) {
encoderRegistry.add(dataClass, encoder);
return this;
}
public <Data, TResource> Registry append(Class<Data> dataClass, Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.append(decoder, dataClass, resourceClass);
return this;
}
public <Data, TResource> Registry prepend(Class<Data> dataClass, Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.prepend(decoder, dataClass, resourceClass);
return this;
}
public <TResource> Registry register(Class<TResource> resourceClass,
ResourceEncoder<TResource> encoder) {
resourceEncoderRegistry.add(resourceClass, encoder);
return this;
}
public Registry register(DataRewinder.Factory factory) {
dataRewinderRegistry.register(factory);
return this;
}
public <TResource, Transcode> Registry register(Class<TResource> resourceClass,
Class<Transcode> transcodeClass, ResourceTranscoder<TResource, Transcode> transcoder) {
transcoderRegistry.register(resourceClass, transcodeClass, transcoder);
return this;
}
public Registry register(ImageHeaderParser parser) {
imageHeaderParserRegistry.add(parser);
return this;
}
/**
* Use the given factory to build a {@link com.bumptech.glide.load.model.ModelLoader} for models
* of the given class. Generally the best use of this method is to replace one of the default
* factories or add an implementation for other similar low level models. Any factory replaced by
* the given factory will have its {@link ModelLoaderFactory#teardown()}} method called.
*
* <p> Note - If a factory already exists for the given class, it will be replaced. If that
* factory is not being used for any other model class, {@link ModelLoaderFactory#teardown()} will
* be called. </p>
*
* <p> Note - The factory must not be an anonymous inner class of an Activity or another object
* that cannot be retained statically. </p>
*
* @param modelClass The model class.
* @param dataClass the data class.
*/
public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
public <Model, Data> Registry prepend(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.prepend(modelClass, dataClass, factory);
return this;
}
public <Model, Data> Registry replace(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.replace(modelClass, dataClass, factory);
return this;
}
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
LoadPath<Data, TResource, Transcode> result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (result == null && !loadPathCache.contains(dataClass, resourceClass, transcodeClass)) {
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
// It's possible there is no way to decode or transcode to the desired types from a given
// data class.
if (decodePaths.isEmpty()) {
result = null;
} else {
result = new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths,
exceptionListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
decodePaths.add(new DecodePath<>(dataClass, registeredResourceClass,
registeredTranscodeClass, decoders, transcoder, exceptionListPool));
}
}
return decodePaths;
}
public <Model, TResource, Transcode> List<Class<?>> getRegisteredResourceClasses(
Class<Model> modelClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
List<Class<?>> result = modelToResourceClassCache.get(modelClass, resourceClass);
if (result == null) {
result = new ArrayList<>();
List<Class<?>> dataClasses = modelLoaderRegistry.getDataClasses(modelClass);
for (Class<?> dataClass : dataClasses) {
List<? extends Class<?>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<?> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry
.getTranscodeClasses(registeredResourceClass, transcodeClass);
if (!registeredTranscodeClasses.isEmpty() && !result.contains(registeredResourceClass)) {
result.add(registeredResourceClass);
}
}
}
modelToResourceClassCache.put(modelClass, resourceClass,
Collections.unmodifiableList(result));
}
return result;
}
public boolean isResourceEncoderAvailable(Resource<?> resource) {
return resourceEncoderRegistry.get(resource.getResourceClass()) != null;
}
public <X> ResourceEncoder<X> getResultEncoder(Resource<X> resource)
throws NoResultEncoderAvailableException {
ResourceEncoder<X> resourceEncoder = resourceEncoderRegistry.get(resource.getResourceClass());
if (resourceEncoder != null) {
return resourceEncoder;
}
throw new NoResultEncoderAvailableException(resource.getResourceClass());
}
@SuppressWarnings("unchecked")
public <X> Encoder<X> getSourceEncoder(X data) throws NoSourceEncoderAvailableException {
Encoder<X> encoder = encoderRegistry.getEncoder((Class<X>) data.getClass());
if (encoder != null) {
return encoder;
}
throw new NoSourceEncoderAvailableException(data.getClass());
}
public <X> DataRewinder<X> getRewinder(X data) {
return dataRewinderRegistry.build(data);
}
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}
public List<ImageHeaderParser> getImageHeaderParsers() {
List<ImageHeaderParser> result = imageHeaderParserRegistry.getParsers();
if (result.isEmpty()) {
throw new NoImageHeaderParserException();
}
return result;
}
/**
* Thrown when no {@link com.bumptech.glide.load.model.ModelLoader} is registered for a given
* model class.
*/
public static class NoModelLoaderAvailableException extends MissingComponentException {
public NoModelLoaderAvailableException(Object model) {
super("Failed to find any ModelLoaders for model: " + model);
}
public NoModelLoaderAvailableException(Class<?> modelClass, Class<?> dataClass) {
super("Failed to find any ModelLoaders for model: " + modelClass + " and data: " + dataClass);
}
}
/**
* Thrown when no {@link ResourceEncoder} is registered for a given resource class.
*/
public static class NoResultEncoderAvailableException extends MissingComponentException {
public NoResultEncoderAvailableException(Class<?> resourceClass) {
super("Failed to find result encoder for resource class: " + resourceClass);
}
}
/**
* Thrown when no {@link Encoder} is registered for a given data class.
*/
public static class NoSourceEncoderAvailableException extends MissingComponentException {
public NoSourceEncoderAvailableException(Class<?> dataClass) {
super("Failed to find source encoder for data class: " + dataClass);
}
}
/**
* Thrown when some necessary component is missing for a load.
*/
public static class MissingComponentException extends RuntimeException {
public MissingComponentException(String message) {
super(message);
}
}
/**
* Thrown when no {@link ImageHeaderParser} is registered.
*/
public static final class NoImageHeaderParserException extends MissingComponentException {
public NoImageHeaderParserException() {
super("Failed to find image header parser.");
}
}
}