EMMA Coverage Report (generated Sat Oct 08 11:41:37 CEST 2011)
[all classes][net.sf.jomic.comic]

COVERAGE SUMMARY FOR SOURCE FILE [ComicModel.java]

nameclass, %method, %block, %line, %
ComicModel.java100% (1/1)100% (14/14)83%  (615/742)89%  (127.7/144)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ComicModel100% (1/1)100% (14/14)83%  (615/742)89%  (127.7/144)
check (JProgressBar): void 100% (1/1)61%  (22/36)58%  (7/12)
getCacheDir (): File 100% (1/1)67%  (8/12)78%  (1.6/2)
ifCanceledThrowException (): void 100% (1/1)67%  (8/12)67%  (2/3)
getCachedFile (String): File 100% (1/1)73%  (11/15)75%  (1.5/2)
computeProgress (int, long): long 100% (1/1)75%  (47/63)87%  (5.2/6)
readImageNames (): void 100% (1/1)76%  (119/157)88%  (25.6/29)
<static initializer> 100% (1/1)80%  (32/40)91%  (2.7/3)
ComicModel (File, ProgressFrame): void 100% (1/1)87%  (268/307)93%  (59.2/64)
ComicModel (File): void 100% (1/1)100% (5/5)100% (2/2)
dispose (): void 100% (1/1)100% (4/4)100% (2/2)
extractImages (boolean): void 100% (1/1)100% (70/70)100% (13/13)
getComicImage (int): ComicImage 100% (1/1)100% (5/5)100% (1/1)
getImage (int): RenderedImage 100% (1/1)100% (12/12)100% (4/4)
getImageCount (): int 100% (1/1)100% (4/4)100% (1/1)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2011 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.comic;
17 
18import java.awt.image.RenderedImage;
19import java.io.File;
20import java.io.IOException;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Iterator;
24import java.util.List;
25 
26import javax.swing.JProgressBar;
27 
28import net.sf.jomic.common.Settings;
29import net.sf.jomic.tools.ArchiveCache;
30import net.sf.jomic.tools.ArchiveCacheEntry;
31import net.sf.jomic.tools.FileArchive;
32import net.sf.jomic.tools.FileTools;
33import net.sf.jomic.tools.ImageTools;
34import net.sf.jomic.tools.LocaleTools;
35import net.sf.jomic.tools.OperationCanceledException;
36import net.sf.jomic.tools.ProgressFrame;
37import net.sf.jomic.tools.StringTools;
38 
39import org.apache.commons.logging.Log;
40import org.apache.commons.logging.LogFactory;
41 
42/**
43 *  Representation of a comic book.
44 *
45 * @author    Thomas Aglassinger
46 */
47public class ComicModel
48{
49    /**
50     *  ID for the task of extracting the images from the archive.
51     */
52    static final int TASK_EXTRACT = 0;
53 
54    /**
55     *  ID for the task of laying out the comic concerning two pages.
56     */
57    static final int TASK_LAYOUT = 2;
58 
59    /**
60     *  ID for the task of analyzing image sizes.
61     */
62    static final int TASK_SIZE = 1;
63 
64    private static final long PROGRESS_WEIGHT_EXTRACT = 9;
65    private static final long PROGRESS_WEIGHT_LAYOUT = 2;
66    private static final long PROGRESS_WEIGHT_SIZE = 4;
67 
68    /**
69     *  The weight of all tasks need to create a ComicModel from a FileArchive.
70     */
71    private static final long[] WEIGHTS = new long[]{
72            PROGRESS_WEIGHT_EXTRACT, PROGRESS_WEIGHT_SIZE, PROGRESS_WEIGHT_LAYOUT};
73 
74    private static Log logger = LogFactory.getLog(ComicModel.class);
75 
76    private FileArchive archive;
77    private ArchiveCache archiveCache;
78    private File archiveCacheDir;
79    private File archiveFile;
80    private ArchiveCacheEntry cacheEntry;
81    private ComicImage[] comicImages;
82    private FileTools fileTools;
83    private List imageNames;
84    private ImageTools imageTools;
85    private LocaleTools localeTools;
86    private ProgressFrame progressFrame;
87 
88    private Settings settings;
89 
90    private StringTools stringTools;
91 
92    /**
93     *  Creates ComicModel from newArchive.
94     *
95     * @throws  IOException           Description of the exception
96     * @throws  InterruptedException  Description of the exception
97     * @param  newArchive             Description of the parameter
98     */
99    // TODO: Remove "Description of" crap once JRefactory's pretty printer is fixed.
100    public ComicModel(File newArchive)
101        throws IOException, InterruptedException {
102        this(newArchive, null);
103    }
104 
105    /**
106     *  Creates ComicModel from newArchive.
107     *
108     * @throws  IOException           Description of the exception
109     * @throws  InterruptedException  Description of the exception
110     * @param  newComicArchiveFile    Description of the parameter
111     * @param  newProgressFrame       Description of the parameter
112     */
113    // TODO: Remove "Description of" crap once JRefactory's pretty printer is fixed.
114    public ComicModel(File newComicArchiveFile, ProgressFrame newProgressFrame)
115        throws IOException, InterruptedException {
116        assert newComicArchiveFile != null;
117 
118        if (logger.isInfoEnabled()) {
119            logger.info("create comic from \"" + newComicArchiveFile + "\"");
120        }
121 
122        settings = Settings.instance();
123        fileTools = FileTools.instance();
124        imageTools = ImageTools.instance();
125        localeTools = LocaleTools.instance();
126        stringTools = StringTools.instance();
127        progressFrame = newProgressFrame;
128 
129        ComicCache comicCache = ComicCache.instance();
130 
131        archiveFile = newComicArchiveFile;
132        archiveCache = comicCache.getArchiveCache();
133        synchronized (archiveCache) {
134            cacheEntry = archiveCache.get(archiveFile);
135            archive = new FileArchive(archiveFile);
136 
137            boolean disposeNeeded = false;
138 
139            try {
140                // Prepare cache for extract.
141                boolean inCache = (cacheEntry != null);
142 
143                if (!inCache) {
144                    cacheEntry = archiveCache.createCacheEntry(archiveFile);
145                    archiveCache.put(cacheEntry);
146                }
147                archiveCacheDir = archiveCache.getCacheEntryDir(cacheEntry);
148                if (!archiveCacheDir.exists()) {
149                    inCache = false;
150                    fileTools.mkdirs(archiveCacheDir);
151                }
152                if (logger.isInfoEnabled()) {
153                    logger.info("cachePath = \"" + archiveCacheDir + "\"");
154                }
155                disposeNeeded = true;
156 
157                readImageNames();
158                comicImages = new ComicImage[imageNames.size()];
159 
160                // Set progress bar limits.
161                if (progressFrame != null) {
162                    progressFrame.setMaximum(computeProgress(TASK_LAYOUT, getImageCount() - 1));
163                }
164 
165                if (inCache) {
166                    long archiveTimestamp = archiveFile.lastModified();
167                    long cacheTimestamp = cacheEntry.getLastModified();
168 
169                    if (archiveTimestamp > cacheTimestamp) {
170                        if (logger.isInfoEnabled()) {
171                            logger.info("archive is newer than cache: archive="
172                                    + archiveTimestamp + ", cache=" + cacheTimestamp);
173                        }
174                        inCache = false;
175                        // TODO: Remove all files in cache entry dir.
176                    } else {
177                        // Validate that all files from the archive exist.
178                        if (progressFrame != null) {
179                            String message = localeTools.getMessage("progress.validating");
180 
181                            progressFrame.setNote(message);
182                        }
183                        Iterator imageNameRider = imageNames.iterator();
184                        int i = 0;
185 
186                        while (imageNameRider.hasNext() && inCache) {
187                            String imageName = (String) imageNameRider.next();
188                            File imageFile = new File(archiveCacheDir, imageName);
189                            boolean imageFileIsInCache = imageFile.exists();
190 
191                            if (!imageFileIsInCache) {
192                                logger.warn("cannot find cached image, re-extracting archive: "
193                                        + stringTools.sourced(imageFile));
194                                inCache = false;
195                            } else {
196                                if (progressFrame != null) {
197                                    progressFrame.setProgress(computeProgress(TASK_EXTRACT, i));
198                                    i += 1;
199                                }
200                            }
201                        }
202                    }
203                }
204                extractImages(inCache);
205                disposeNeeded = false;
206            } finally {
207                if (disposeNeeded) {
208                    // TODO: Use cache.remove(entry).
209                    fileTools.attemptToDeleteAll(archiveCacheDir, logger);
210                } else {
211                    archiveCache.setCacheEntrySize(cacheEntry);
212                    cacheEntry.lock();
213                }
214            }
215 
216        }
217    }
218 
219    /**
220     *  Get image at index.
221     *
222     * @see    #getComicImage(int)
223     */
224    public final RenderedImage getImage(int index) {
225        RenderedImage result = null;
226        ComicImage comicImage = comicImages[index];
227 
228        result = comicImage.getImage();
229        return result;
230    }
231 
232    /**
233     *  Get number of images.
234     */
235    public final int getImageCount() {
236        return imageNames.size();
237    }
238 
239    /**
240     *  Get directory where extracted copies of images are stored.
241     */
242    public File getCacheDir() {
243        // Ensure post condition.
244        assert archiveCacheDir != null;
245        return archiveCacheDir;
246    }
247 
248    /**
249     *  Get ComicImage at index.
250     *
251     * @see    #getImage(int)
252     */
253    public ComicImage getComicImage(int index) {
254        return comicImages[index];
255    }
256 
257    /**
258     *  Get file named fileName in cache path.
259     */
260    private File getCachedFile(final String fileName) {
261        assert fileName != null;
262        return new File(archiveCacheDir, fileName);
263    }
264 
265    /**
266     *  Release all resources related to the comic.
267     */
268    public final void dispose() {
269        // TODO: Check if cache became to big and entry should be removed.
270        cacheEntry.unlock();
271    }
272 
273    /**
274     *  Attempt to extract and parse all images.
275     */
276    public void check(JProgressBar bar) {
277        if (bar != null) {
278            bar.setMinimum(0);
279            bar.setMaximum(getImageCount());
280        }
281        for (int i = 0; i < getImageCount(); i += 1) {
282            if (bar != null) {
283                bar.setValue(i);
284            }
285            ComicImage image = comicImages[i];
286 
287            try {
288                image.getImage();
289            } catch (Exception e) {
290                image.setError(e);
291            }
292        }
293    }
294 
295    /**
296     *  Compute progress considering weights of sub-tasks.
297     */
298    long computeProgress(int task, long value) {
299        assert (task == TASK_EXTRACT)
300                || (task == TASK_SIZE)
301                || (task == TASK_LAYOUT) : "type=" + task;
302        assert (value >= 0) && (value < getImageCount());
303        long result;
304 
305        result = value * WEIGHTS[task];
306        for (int i = 0; i < task; i += 1) {
307            result += getImageCount() * WEIGHTS[i];
308        }
309        return result;
310    }
311 
312    /**
313     *  Extract all images to cache.
314     */
315    private void extractImages(boolean isInCache)
316        throws IOException {
317        if (!isInCache) {
318            // TODO: Extract to some temp path first, then rename it.
319            // Reason: if the same archive is opened twice, but it fails the
320            // second time, then FileArchive deletes the files for the reverted
321            // archive.
322            archive.extract(archiveCacheDir, (String[]) imageNames.toArray(
323                    new String[0]), progressFrame, PROGRESS_WEIGHT_EXTRACT);
324        }
325 
326        if (progressFrame != null) {
327            String message = localeTools.getMessage("panels.comic.progress.analyzingImages");
328 
329            progressFrame.setNote(message);
330        }
331        for (int i = 0; i < comicImages.length; i += 1) {
332            String fileName = (String) imageNames.get(i);
333            File file = getCachedFile(fileName);
334 
335            ifCanceledThrowException();
336            if (progressFrame != null) {
337                progressFrame.setProgress(computeProgress(TASK_SIZE, i));
338            }
339 
340            comicImages[i] = new ComicImage(file);
341        }
342    }
343 
344    private void ifCanceledThrowException() {
345        if ((progressFrame != null) && progressFrame.isCanceled()) {
346            throw new OperationCanceledException();
347        }
348    }
349 
350    /**
351     *  Read the archive's table of contents, check for images, and collect the fitting file names
352     *  in <code>imageNames</code>.
353     */
354    private void readImageNames()
355        throws IOException {
356        if (progressFrame != null) {
357            String message = localeTools.getMessage("panels.comic.progress.examining");
358 
359            progressFrame.setNote(message);
360        }
361 
362        String[] archiveContents = archive.list();
363 
364        imageNames = new ArrayList();
365        for (int i = 0; i < archiveContents.length; i += 1) {
366            String fileName = archiveContents[i];
367 
368            if (imageTools.isImageFile(fileName)) {
369                if (logger.isInfoEnabled()) {
370                    logger.info("found image: \"" + fileName + "\"");
371                }
372                imageNames.add(fileName);
373            } else {
374                if (logger.isDebugEnabled()) {
375                    logger.debug("ignoring file: \"" + fileName + "\"");
376                }
377            }
378        }
379        ((ArrayList) imageNames).trimToSize();
380 
381        // Sort image names.
382        String sortMode = settings.getSortMode();
383 
384        if (logger.isDebugEnabled()) {
385            logger.debug("sorted list of names (mode=" + sortMode + ":");
386        }
387        String[] imageNamesAsArray = (String[]) imageNames.toArray(new String[0]);
388 
389        imageNames = Arrays.asList(fileTools.sort(imageNamesAsArray, sortMode));
390 
391        Iterator imageRider = imageNames.iterator();
392 
393        while (imageRider.hasNext()) {
394            String name = (String) imageRider.next();
395 
396            if (logger.isDebugEnabled()) {
397                logger.debug("  " + name);
398            }
399        }
400        assert imageNames != null;
401        if (imageNames.size() == 0) {
402            throw new ComicMustContainImagesException(archiveContents);
403        }
404    }
405}

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