package com.bumptech.glide.load.resource.transcode; import com.bumptech.glide.util.Synthetic; import java.util.ArrayList; import java.util.List; /** * A class that allows {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder}s to be * registered and retrieved by the classes they convert between. */ public class TranscoderRegistry { private final List<Entry<?, ?>> transcoders = new ArrayList<>(); /** * Registers the given {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} using * the given classes so it can later be retrieved using the given classes. * * @param decodedClass The class of the resource that the transcoder transcodes from. * @param transcodedClass The class of the resource that the transcoder transcodes to. * @param transcoder The transcoder. * @param <Z> The type of the resource that the transcoder transcodes from. * @param <R> The type of the resource that the transcoder transcodes to. */ public synchronized <Z, R> void register(Class<Z> decodedClass, Class<R> transcodedClass, ResourceTranscoder<Z, R> transcoder) { transcoders.add(new Entry<>(decodedClass, transcodedClass, transcoder)); } /** * Returns the currently registered * {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} for the given classes. * * @param resourceClass The class of the resource that the transcoder transcodes from. * @param transcodedClass The class of the resource that the transcoder transcodes to. * @param <Z> The type of the resource that the transcoder transcodes from. * @param <R> The type of the resource that the transcoder transcodes to. */ @SuppressWarnings("unchecked") public synchronized <Z, R> ResourceTranscoder<Z, R> get(Class<Z> resourceClass, Class<R> transcodedClass) { // For example, there may be a transcoder that can convert a GifDrawable to a Drawable, which // will be caught above. However, if there is no registered transcoder, we can still just use // the UnitTranscoder to return the Drawable because the transcode class (Drawable) is // assignable from the resource class (GifDrawable). if (transcodedClass.isAssignableFrom(resourceClass)) { return (ResourceTranscoder<Z, R>) UnitTranscoder.get(); } for (Entry<?, ?> entry : transcoders) { if (entry.handles(resourceClass, transcodedClass)) { return (ResourceTranscoder<Z, R>) entry.transcoder; } } throw new IllegalArgumentException( "No transcoder registered to transcode from " + resourceClass + " to " + transcodedClass); } public synchronized <Z, R> List<Class<R>> getTranscodeClasses(Class<Z> resourceClass, Class<R> transcodeClass) { List<Class<R>> transcodeClasses = new ArrayList<>(); // GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable. if (transcodeClass.isAssignableFrom(resourceClass)) { transcodeClasses.add(transcodeClass); return transcodeClasses; } for (Entry<?, ?> entry : transcoders) { if (entry.handles(resourceClass, transcodeClass)) { transcodeClasses.add(transcodeClass); } } return transcodeClasses; } private static final class Entry<Z, R> { private final Class<Z> fromClass; private final Class<R> toClass; @Synthetic final ResourceTranscoder<Z, R> transcoder; Entry(Class<Z> fromClass, Class<R> toClass, ResourceTranscoder<Z, R> transcoder) { this.fromClass = fromClass; this.toClass = toClass; this.transcoder = transcoder; } /** * If we convert from a specific Drawable, we must get that specific Drawable class or a * subclass of that Drawable. In contrast, if we we convert <em>to</em> a specific Drawable, * we can fulfill requests for a more generic parent class (like Drawable). As a result, we * check fromClass and toClass in different orders. */ public boolean handles(Class<?> fromClass, Class<?> toClass) { return this.fromClass.isAssignableFrom(fromClass) && toClass.isAssignableFrom(this.toClass); } } }