1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Oleg V. Khaschansky
19 * @version $Revision$
20 */
21/*
22 * Created on 18.01.2005
23 */
24package org.apache.harmony.awt.gl.image;
25
26import java.security.AccessController;
27import java.security.PrivilegedAction;
28import java.util.ArrayList;
29import java.util.LinkedList;
30import java.util.List;
31
32/**
33 * This class provides functionality for simultaneous loading of
34 * several images and running animation.
35 */
36public class ImageLoader extends Thread {
37    // Contains ImageLoader objects
38    // and queue of image sources waiting to be loaded
39    static class ImageLoadersStorage {
40        private static final int MAX_THREADS = 5;
41        private static final int TIMEOUT = 4000;
42        static ImageLoadersStorage instance;
43
44        List<DecodingImageSource> queue = new LinkedList<DecodingImageSource>();
45        List<Thread> loaders = new ArrayList<Thread>(MAX_THREADS);
46
47        private int freeLoaders;
48
49        private ImageLoadersStorage() {}
50
51        static ImageLoadersStorage getStorage() {
52            if (instance == null) {
53                instance = new ImageLoadersStorage();
54            }
55
56            return instance;
57        }
58    }
59
60    ImageLoader() {
61        super();
62        setDaemon(true);
63    }
64
65    /**
66     * This method creates a new thread which is able to load an image
67     * or run animation (if the number of existing loader threads does not
68     * exceed the limit).
69     */
70    private static void createLoader() {
71        final ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
72
73        synchronized(storage.loaders) {
74            if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS) {
75                AccessController.doPrivileged(
76                        new PrivilegedAction<Void>() {
77                            public Void run() {
78                                ImageLoader loader = new ImageLoader();
79                                storage.loaders.add(loader);
80                                loader.start();
81                                return null;
82                            }
83                        });
84            }
85        }
86    }
87
88    /**
89     * Adds a new image source to the queue and starts a new loader
90     * thread if required
91     * @param imgSrc - image source
92     */
93    public static void addImageSource(DecodingImageSource imgSrc) {
94        ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
95        synchronized(storage.queue) {
96            if (!storage.queue.contains(imgSrc)) {
97                storage.queue.add(imgSrc);
98            }
99            if (storage.freeLoaders == 0) {
100                createLoader();
101            }
102
103            storage.queue.notify();
104        }
105    }
106
107    /**
108     * Waits for a new ImageSource until timout expires.
109     * Loader thread will terminate after returning from this method
110     * if timeout expired and image source was not picked up from the queue.
111     * @return image source picked up from the queue or null if timeout expired
112     */
113    private static DecodingImageSource getWaitingImageSource() {
114        ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
115
116        synchronized(storage.queue) {
117            DecodingImageSource isrc = null;
118
119            if (storage.queue.size() == 0) {
120                try {
121                    storage.freeLoaders++;
122                    storage.queue.wait(ImageLoadersStorage.TIMEOUT);
123                } catch (InterruptedException e) {
124                    return null;
125                } finally {
126                    storage.freeLoaders--;
127                }
128            }
129
130            if (storage.queue.size() > 0) {
131                isrc = storage.queue.get(0);
132                storage.queue.remove(0);
133            }
134
135            return isrc;
136        }
137    }
138
139    /**
140     * Entry point of the loader thread. Picks up image sources and
141     * runs decoders for them while there are available image sources in the queue.
142     * If there are no and timeout expires it terminates.
143     */
144    @Override
145    public void run() {
146        ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
147
148        try {
149            while (storage.loaders.contains(this)) {
150                Thread.interrupted(); // Reset the interrupted flag
151                DecodingImageSource isrc = getWaitingImageSource();
152                if (isrc != null) {
153                    try {
154                        isrc.load();
155                    } catch (Exception e) {
156                        e.printStackTrace();
157                    }
158                } else {
159                    break; // Don't wait if timeout expired - terminate loader
160                }
161            }
162        } catch (Exception e) {
163            e.printStackTrace();
164        } finally {
165            synchronized(storage.loaders) {
166                storage.loaders.remove(Thread.currentThread());
167            }
168        }
169    }
170
171    /**
172     * Removes current thread from loaders (so we are able
173     * to create more loaders) and decreases its priority.
174     */
175    static void beginAnimation() {
176        ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
177        Thread currThread = Thread.currentThread();
178
179        synchronized(storage) {
180            storage.loaders.remove(currThread);
181
182            if (storage.freeLoaders < storage.queue.size()) {
183                createLoader();
184            }
185        }
186
187        currThread.setPriority(Thread.MIN_PRIORITY);
188    }
189
190    /**
191     * Sends the current thread to wait for the new images to load
192     * if there are free placeholders for loaders
193     */
194    static void endAnimation() {
195        ImageLoadersStorage storage = ImageLoadersStorage.getStorage();
196        Thread currThread = Thread.currentThread();
197
198        synchronized(storage) {
199            if (storage.loaders.size() < ImageLoadersStorage.MAX_THREADS &&
200                    !storage.loaders.contains(currThread)
201            ) {
202                storage.loaders.add(currThread);
203            }
204        }
205
206        currThread.setPriority(Thread.NORM_PRIORITY);
207    }
208}