1package com.bumptech.glide.load.engine;
2
3import android.os.SystemClock;
4import android.util.Log;
5import com.bumptech.glide.load.CacheLoader;
6import com.bumptech.glide.Priority;
7import com.bumptech.glide.load.Encoder;
8import com.bumptech.glide.load.ResourceDecoder;
9import com.bumptech.glide.load.ResourceEncoder;
10import com.bumptech.glide.load.Transformation;
11import com.bumptech.glide.load.data.DataFetcher;
12import com.bumptech.glide.load.engine.cache.DiskCache;
13import com.bumptech.glide.load.engine.executor.Prioritized;
14import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
15import com.bumptech.glide.request.ResourceCallback;
16
17import java.io.InputStream;
18import java.io.OutputStream;
19
20/**
21 *
22 * @param <T> The type of the data the resource will be decoded from.
23 * @param <Z> The type of the resource that will be decoded.
24 * @param <R> The type of the resource that will be transcoded to from the decoded resource.
25 */
26public class SourceResourceRunner<T, Z, R> implements Runnable, DiskCache.Writer, Prioritized {
27    private static final String TAG = "SourceRunner";
28    private final EngineKey key;
29    private final int width;
30    private final int height;
31    private final CacheLoader cacheLoader;
32    private final ResourceDecoder<InputStream, Z> cacheDecoder;
33    private final DataFetcher<T> fetcher;
34    private final boolean cacheSource;
35    private final Encoder<T> sourceEncoder;
36    private final ResourceDecoder<T, Z> decoder;
37    private final Transformation<Z> transformation;
38    private final ResourceEncoder<Z> encoder;
39    private final ResourceTranscoder<Z, R> transcoder;
40    private final DiskCache diskCache;
41    private final Priority priority;
42    private final ResourceCallback cb;
43
44    private Resource<Z> result;
45    private volatile boolean isCancelled;
46
47    public SourceResourceRunner(EngineKey key, int width, int height, CacheLoader cacheLoader,
48            ResourceDecoder<InputStream, Z> cacheDecoder, DataFetcher<T> dataFetcher, boolean cacheSource,
49            Encoder<T> sourceEncoder, ResourceDecoder<T, Z> decoder, Transformation<Z> transformation,
50            ResourceEncoder<Z> encoder, ResourceTranscoder<Z, R> transcoder, DiskCache diskCache, Priority priority,
51            ResourceCallback cb) {
52        this.key = key;
53        this.width = width;
54        this.height = height;
55        this.cacheLoader = cacheLoader;
56        this.cacheDecoder = cacheDecoder;
57        this.fetcher = dataFetcher;
58        this.cacheSource = cacheSource;
59        this.sourceEncoder = sourceEncoder;
60        this.decoder = decoder;
61        this.transformation = transformation;
62        this.encoder = encoder;
63        this.transcoder = transcoder;
64        this.diskCache = diskCache;
65        this.priority = priority;
66        this.cb = cb;
67    }
68
69    public void cancel() {
70        isCancelled = true;
71        if (fetcher != null) {
72            fetcher.cancel();
73        }
74    }
75
76    @Override
77    public void run() {
78        if (isCancelled) {
79            return;
80        }
81
82        try {
83            long start = SystemClock.currentThreadTimeMillis();
84            Resource<Z> decoded = cacheLoader.load(key.getOriginalKey(), cacheDecoder, width, height);
85
86            if (decoded == null) {
87                decoded = decodeFromSource();
88                if (Log.isLoggable(TAG, Log.VERBOSE)) {
89                    Log.v(TAG, "Decoded from source in " + (SystemClock.currentThreadTimeMillis() - start) + " cache");
90                    start = SystemClock.currentThreadTimeMillis();
91                }
92            }
93
94            if (decoded != null) {
95                Resource<Z> transformed = transformation.transform(decoded, width, height);
96                if (decoded != transformed) {
97                    decoded.recycle();
98                }
99                result = transformed;
100            }
101            if (Log.isLoggable(TAG, Log.VERBOSE)) {
102                Log.v(TAG, "transformed in " + (SystemClock.currentThreadTimeMillis() - start));
103            }
104
105            if (result != null) {
106                diskCache.put(key, this);
107                start = SystemClock.currentThreadTimeMillis();
108                Resource<R> transcoded = transcoder.transcode(result);
109                if (Log.isLoggable(TAG, Log.VERBOSE)) {
110                    Log.d(TAG, "transcoded in " + (SystemClock.currentThreadTimeMillis() - start));
111                }
112                cb.onResourceReady(transcoded);
113            } else {
114                cb.onException(null);
115            }
116
117        } catch (Exception e) {
118            cb.onException(e);
119        }
120    }
121
122    private Resource<Z> encodeSourceAndDecodeFromCache(final T data) {
123        diskCache.put(key.getOriginalKey(), new DiskCache.Writer() {
124            @Override
125            public boolean write(OutputStream os) {
126                return sourceEncoder.encode(data, os);
127            }
128        });
129        return cacheLoader.load(key.getOriginalKey(), cacheDecoder, width, height);
130    }
131
132    private Resource<Z> decodeFromSource() throws Exception {
133        try {
134            final T data = fetcher.loadData(priority);
135            if (data != null) {
136                if (cacheSource) {
137                    return encodeSourceAndDecodeFromCache(data);
138                } else {
139                    return decoder.decode(data, width, height);
140                }
141            }
142        } finally {
143            fetcher.cleanup();
144        }
145
146        return null;
147    }
148
149    @Override
150    public boolean write(OutputStream os) {
151        long start = SystemClock.currentThreadTimeMillis();
152        boolean success = encoder.encode(result, os);
153        if (Log.isLoggable(TAG, Log.VERBOSE)) {
154            Log.v(TAG, "wrote to disk cache in " + (SystemClock.currentThreadTimeMillis() - start));
155        }
156        return success;
157    }
158
159    @Override
160    public int getPriority() {
161        return priority.ordinal();
162    }
163}
164