EMMA Coverage Report (generated Sun Apr 20 22:38:01 CEST 2008)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [ImageCacheRenderThread.java]

nameclass, %method, %block, %line, %
ImageCacheRenderThread.java100% (2/2)92%  (11/12)73%  (290/399)80%  (76/95)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ImageCacheRenderThread$ImageRenderTask100% (1/1)100% (5/5)58%  (50/86)81%  (10.5/13)
ImageCacheRenderThread$ImageRenderTask (ImageCacheRenderThread, File, ImageIn... 100% (1/1)53%  (33/62)77%  (7/9)
<static initializer> 100% (1/1)53%  (8/15)53%  (0.5/1)
getImageFile (): File 100% (1/1)100% (3/3)100% (1/1)
getListener (): ImageInCacheListener 100% (1/1)100% (3/3)100% (1/1)
getPriority (): int 100% (1/1)100% (3/3)100% (1/1)
     
class ImageCacheRenderThread100% (1/1)86%  (6/7)77%  (240/313)80%  (65.5/82)
clear (): void 0%   (0/1)0%   (0/17)0%   (0/4)
dispose (): void 100% (1/1)58%  (14/24)61%  (5.5/9)
ImageCacheRenderThread (ImageCache): void 100% (1/1)79%  (33/42)92%  (6.4/7)
run (): void 100% (1/1)80%  (67/84)82%  (21.4/26)
<static initializer> 100% (1/1)80%  (12/15)80%  (0.8/1)
addTask (File, ImageInCacheListener, int): void 100% (1/1)82%  (79/96)86%  (21.4/25)
findIndexOfExistingSpecification (File, ImageInCacheListener): int 100% (1/1)100% (35/35)100% (10/10)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2008 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.tools;
17 
18import java.io.File;
19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
22 
23import net.sf.wraplog.Logger;
24 
25/**
26 *  Thread to render images in the background, put them in a cache and notify a listener when done.
27 *
28 * @author    Thomas Aglassinger
29 * @see       net.sf.jomic.tools.ImageInCacheListener
30 * @see       net.sf.jomic.tools.ImageCache
31 */
32class ImageCacheRenderThread extends Thread
33{
34    /**
35     *  Priority indicating an image should be rendered as soon as possible.
36     */
37    static final int PRIORITY_NOW = Thread.NORM_PRIORITY;
38 
39    /**
40     *  Priority indicating an image should be rendered when there are no other images left.
41     */
42    static final int PRIORITY_SOMETIMES = Thread.MIN_PRIORITY;
43 
44    /**
45     *  Priority indicating an image should be rendered once all urgent requests have been
46     *  processed.
47     */
48    static final int PRIORITY_SOON = Thread.NORM_PRIORITY - 1;
49 
50    /**
51     *  Tick for (ugly) polling loops.
52     */
53    private static final int CACHE_TICK = 10;
54    private static final int NO_INDEX = -1;
55 
56    private ImageCache cache;
57    private boolean disposed;
58    private Logger logger;
59    private List openTasks;
60 
61    ImageCacheRenderThread(ImageCache newCache) {
62        super();
63        assert newCache != null;
64        logger = Logger.getLogger(ImageCacheRenderThread.class);
65        openTasks = new LinkedList();
66        cache = newCache;
67        setName("renderer." + cache.getName());
68    }
69 
70    public void run() {
71        while (!disposed) {
72            ImageRenderTask specificationToRender = null;
73 
74            synchronized (openTasks) {
75                int indexOfNextSpecification = openTasks.size() - 1;
76 
77                if (indexOfNextSpecification >= 0) {
78                    // Schedule and remove the last specification in the list.
79                    specificationToRender = (ImageRenderTask)
80                            openTasks.get(indexOfNextSpecification);
81                    openTasks.remove(indexOfNextSpecification);
82                    logger.info("scheduling image to be rendered: {}", specificationToRender.getImageFile());
83                }
84            }
85            if (specificationToRender != null) {
86                File imageFile = specificationToRender.getImageFile();
87                int priority = specificationToRender.getPriority();
88                ImageInCacheListener listener = specificationToRender.getListener();
89 
90                setPriority(priority);
91                try {
92                    cache.get(imageFile);
93                } catch (Throwable error) {
94                    logger.warn("cannot render image, notifying listener anyway", error);
95                }
96                listener.imageCached(imageFile);
97            } else {
98                try {
99                    Thread.sleep(CACHE_TICK);
100                } catch (InterruptedException error) {
101                    logger.warn("sleep interrupted, continuing", error);
102                }
103            }
104        }
105    }
106 
107    /**
108     *  Schedule image to be rendered, and notify the specified listener when ready.
109     *
110     * @see                       #PRIORITY_NOW
111     * @see                       #PRIORITY_SOMETIMES
112     * @see                       #PRIORITY_SOON
113     * @param  imageFileToRender  the image to be scheduled for rendering
114     * @param  listener           ImageCacheListener to be notified once the image is rendered and
115     *      ready to be displayed
116     * @param  priority           one of: PRIORITY_*
117     */
118    void addTask(File imageFileToRender, ImageInCacheListener listener, int priority) {
119        logger.info("add task for image: {}", imageFileToRender);
120        if (!disposed) {
121            synchronized (openTasks) {
122                boolean addSpecification = true;
123                int indexOfExistingSpecification = findIndexOfExistingSpecification(imageFileToRender, listener);
124 
125                if (indexOfExistingSpecification != NO_INDEX) {
126                    ImageRenderTask existingSpecification = (ImageRenderTask)
127                            openTasks.get(indexOfExistingSpecification);
128 
129                    if (existingSpecification.getPriority() >= priority) {
130                        // No need to add specification, we already render this file with a higher priority
131                        addSpecification = false;
132                    } else {
133                        // The new specification has a higher priority, so remove the old one before adding the new.
134                        openTasks.remove(indexOfExistingSpecification);
135                    }
136                }
137 
138                if (addSpecification) {
139                    boolean indexFound = false;
140                    int index = 0;
141                    Iterator rider = openTasks.iterator();
142 
143                    while (rider.hasNext() && !indexFound) {
144                        ImageRenderTask specification = (ImageRenderTask) rider.next();
145 
146                        if (priority < specification.getPriority()) {
147                            indexFound = true;
148                        } else {
149                            index += 1;
150                        }
151                    }
152 
153                    ImageRenderTask newSpecification =
154                            new ImageRenderTask(imageFileToRender, listener, priority);
155 
156                    openTasks.add(index, newSpecification);
157                }
158            }
159        } else {
160            logger.warn("refusing to add task because thread is already in process of being disposed");
161        }
162    }
163 
164    /**
165     *  Remove all scheduled tasks. The task currently processed will still be finished, though.
166     */
167    void clear() {
168        synchronized (openTasks) {
169            openTasks.clear();
170        }
171    }
172 
173    void dispose() {
174        // TODO: assert that dispose is not called by ImageCacheRenderThread.
175        logger.info("disposing");
176        if (openTasks != null) {
177            disposed = true;
178            try {
179                this.join();
180            } catch (InterruptedException error) {
181                // There is nothing really bad about that, but it might cause a few
182                // AssertionErrors if a task is still processed.
183                logger.error("dipose interrupted", error);
184            }
185        } else {
186            disposed = true;
187        }
188    }
189 
190    private int findIndexOfExistingSpecification(File imageFile, ImageInCacheListener listener) {
191        int result = NO_INDEX;
192        int index = 0;
193        Iterator rider = openTasks.iterator();
194 
195        while (rider.hasNext() && (result == NO_INDEX)) {
196            ImageRenderTask specification = (ImageRenderTask) rider.next();
197 
198            if (specification.getImageFile().equals(imageFile) && (specification.listener.equals(listener))) {
199                result = index;
200            } else {
201                index += 1;
202            }
203        }
204        return result;
205    }
206 
207    /**
208     *  Specifies a task to be scheduled for the render thread.
209     *
210     * @author    Thomas Aglassinger
211     * @see       ImageCacheRenderThread
212     */
213    private class ImageRenderTask
214    {
215        private File imageFile;
216        private ImageInCacheListener listener;
217        private int priority;
218 
219        /**
220         *  Create a new task for the render thread.
221         *
222         * @param  newImageFile  the file that contains the image to be rendered
223         * @param  newListener   the listener to be notified when the image is rendered
224         * @param  newPriority   the priority to render with (one of <code>PRIORITY_xxx</code>)
225         */
226        ImageRenderTask(File newImageFile, ImageInCacheListener newListener, int newPriority) {
227            assert newImageFile != null;
228            assert newListener != null;
229            assert newPriority <= PRIORITY_NOW;
230            assert newPriority >= PRIORITY_SOMETIMES
231                    : "newPriority=" + priority + " must be at least PRIORITY_SOMETIMES=" + PRIORITY_SOMETIMES;
232 
233            imageFile = newImageFile;
234            listener = newListener;
235            priority = newPriority;
236        }
237 
238        File getImageFile() {
239            return imageFile;
240        }
241 
242        ImageInCacheListener getListener() {
243            return listener;
244        }
245 
246        int getPriority() {
247            return priority;
248        }
249    }
250}

[all classes][net.sf.jomic.tools]
EMMA 2.0.4217 (C) Vladimir Roubtsov