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

COVERAGE SUMMARY FOR SOURCE FILE [ComicModel.java]

nameclass, %method, %block, %line, %
ComicModel.java100% (1/1)100% (14/14)87%  (649/742)91%  (131.7/145)

COVERAGE BREAKDOWN BY CLASS AND METHOD

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

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