| 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/>. | 
| 16 | package net.sf.jomic.comic; | 
| 17 |   | 
| 18 | import java.io.File; | 
| 19 | import java.util.HashMap; | 
| 20 | import java.util.Map; | 
| 21 |   | 
| 22 | import net.sf.jomic.tools.AbstractTask; | 
| 23 | import net.sf.jomic.tools.FileTools; | 
| 24 | import net.sf.jomic.tools.IOExceptionWithCause; | 
| 25 | import net.sf.jomic.tools.ImageInfo; | 
| 26 | import net.sf.jomic.tools.LocaleTools; | 
| 27 | import net.sf.jomic.tools.StringTools; | 
| 28 |   | 
| 29 | import org.apache.commons.logging.Log; | 
| 30 | import org.apache.commons.logging.LogFactory; | 
| 31 |   | 
| 32 | /** | 
| 33 |  *  Task to create comic from a set of image files. | 
| 34 |  * | 
| 35 |  * @author    Thomas Aglassinger | 
| 36 |  */ | 
| 37 | public abstract class AbstractCreateComicTask extends AbstractTask | 
| 38 | { | 
| 39 |     private Conversion conversion; | 
| 40 |     private FileTools fileTools; | 
| 41 |     private Map imageInfoMap; | 
| 42 |     private LocaleTools localeTools; | 
| 43 |     private Log logger; | 
| 44 |     private File sourceBaseDir; | 
| 45 |     private String[] sourceFileNames; | 
| 46 |     private StringTools stringTools; | 
| 47 |     private File targetComicFile; | 
| 48 |     private boolean wroteAnyImages; | 
| 49 |   | 
| 50 |     /** | 
| 51 |      *  Create a new task to create a comic. | 
| 52 |      * | 
| 53 |      * @see                        ImageInfo | 
| 54 |      * @param  newSourceBaseDir    the base directory where the source files are located | 
| 55 |      * @param  newSourceFileNames  the names of the files relative to <code>newSourceBaseDir</code> | 
| 56 |      *      that should be included in the archive | 
| 57 |      * @param  newImageInfoMap     a map of <code>ImageInfos</code> used to decide if images can be | 
| 58 |      *      added. If this is <code>null</code>, the <code>ImageInfo</code> is filled during <code>start()</code> | 
| 59 |      *      . | 
| 60 |      * @param  newTargetComicFile  the comic file to create | 
| 61 |      * @param  newConversion       a conversion specifying the format and image manipulations of the | 
| 62 |      *      new comic; if null, the default will be a conversion creating a CBZ including all the | 
| 63 |      *      images specified. | 
| 64 |      */ | 
| 65 |     public AbstractCreateComicTask(File newSourceBaseDir, String[] newSourceFileNames, | 
| 66 |             Map newImageInfoMap, File newTargetComicFile, Conversion newConversion) { | 
| 67 |         this(); | 
| 68 |         assert newTargetComicFile != null; | 
| 69 |         assert newSourceBaseDir != null; | 
| 70 |         assert newSourceFileNames != null; | 
| 71 |         assert newSourceFileNames.length > 0; | 
| 72 |         assert newConversion != null; | 
| 73 |         sourceBaseDir = newSourceBaseDir; | 
| 74 |         sourceFileNames = newSourceFileNames; | 
| 75 |         if (newImageInfoMap == null) { | 
| 76 |             imageInfoMap = new HashMap(); | 
| 77 |         } else { | 
| 78 |             imageInfoMap = newImageInfoMap; | 
| 79 |         } | 
| 80 |         targetComicFile = newTargetComicFile; | 
| 81 |         conversion = newConversion; | 
| 82 |         for (int i = 0; i < sourceFileNames.length; i += 1) { | 
| 83 |             File sourceFile = new File(sourceBaseDir, sourceFileNames[i]); | 
| 84 |   | 
| 85 |             if (!sourceFile.isFile()) { | 
| 86 |                 String message = localeTools.getMessage( | 
| 87 |                         "errors.itemAddedToZipArchiveMustBeFile", stringTools.sourced(sourceFile)); | 
| 88 |   | 
| 89 |                 throw new IllegalArgumentException(message); | 
| 90 |             } | 
| 91 |             setMaxProgress(getMaxProgress() + 1 + sourceFile.length()); | 
| 92 |         } | 
| 93 |     } | 
| 94 |   | 
| 95 |     private AbstractCreateComicTask() { | 
| 96 |         super(); | 
| 97 |         logger = LogFactory.getLog(this.getClass()); | 
| 98 |         fileTools = FileTools.instance(); | 
| 99 |         localeTools = LocaleTools.instance(); | 
| 100 |         stringTools = StringTools.instance(); | 
| 101 |     } | 
| 102 |   | 
| 103 |     /** | 
| 104 |      *  Allocate all resources needed to create the target comic file. They will be released when | 
| 105 |      *  done by calling <code>cleanUpComic()</code> either when done or an error occurs. | 
| 106 |      * | 
| 107 |      * @see    #cleanUpComic() | 
| 108 |      */ | 
| 109 |     protected abstract void setUpComic() | 
| 110 |         throws Exception; | 
| 111 |   | 
| 112 |     protected final Conversion getConversion() { | 
| 113 |         return conversion; | 
| 114 |     } | 
| 115 |   | 
| 116 |     protected final File getSourceBaseDir() { | 
| 117 |         return sourceBaseDir; | 
| 118 |     } | 
| 119 |   | 
| 120 |     protected final File getTargetComicFile() { | 
| 121 |         return targetComicFile; | 
| 122 |     } | 
| 123 |   | 
| 124 |     /** | 
| 125 |      *  Obtain the <code>imageInfo</code> for <code>imageFile</code>, preferably from the <code>newImageInfoMap</code> | 
| 126 |      *  passed to the constructor. If the image can not be found in the map, obtain a new <code>ImageInfo</code> | 
| 127 |      *  for it and put it in the map for later reuse. | 
| 128 |      */ | 
| 129 |     protected ImageInfo getImageInfo(File imageFile) { | 
| 130 |         ImageInfo result = (ImageInfo) imageInfoMap.get(imageFile); | 
| 131 |   | 
| 132 |         if (result == null) { | 
| 133 |             result = new ImageInfo(imageFile); | 
| 134 |             imageInfoMap.put(imageFile, result); | 
| 135 |         } | 
| 136 |         return result; | 
| 137 |     } | 
| 138 |   | 
| 139 |     public void start() | 
| 140 |         throws Exception { | 
| 141 |         boolean allFilesProcessed = false; | 
| 142 |   | 
| 143 |         if (logger.isInfoEnabled()) { | 
| 144 |             logger.info("create comic: " + targetComicFile); | 
| 145 |         } | 
| 146 |         String creatingMessage = localeTools.getMessage("progress.create.creating", targetComicFile.getName()); | 
| 147 |   | 
| 148 |         setProgressMessage(creatingMessage); | 
| 149 |   | 
| 150 |         File targetFolder = targetComicFile.getParentFile(); | 
| 151 |   | 
| 152 |         fileTools.mkdirs(targetFolder); | 
| 153 |         setUpComic(); | 
| 154 |         try { | 
| 155 |             setProgress(0); | 
| 156 |             wroteAnyImages = false; | 
| 157 |             for (int i = 0; !isInterrupted() && (i < sourceFileNames.length); i += 1) { | 
| 158 |                 String sourceName = sourceFileNames[i]; | 
| 159 |                 File sourceImageFile = new File(sourceBaseDir, sourceName); | 
| 160 |                 ImageInfo imageInfo = getImageInfo(sourceImageFile); | 
| 161 |                 String imageFormat = imageInfo.getFormat(); | 
| 162 |                 String outName = sourceFileNames[i]; | 
| 163 |   | 
| 164 |                 outName = fileTools.getWithoutLastSuffix(outName); | 
| 165 |                 outName = outName + "." + conversion.getImageFormatName(imageFormat); | 
| 166 |                 addImageFile(outName, sourceImageFile); | 
| 167 |             } | 
| 168 |             allFilesProcessed = !isInterrupted(); | 
| 169 |         } finally { | 
| 170 |             Throwable cause = null; | 
| 171 |   | 
| 172 |             try { | 
| 173 |                 cleanUpComic(); | 
| 174 |             } catch (Throwable error) { | 
| 175 |                 cause = error; | 
| 176 |             } | 
| 177 |             if (!allFilesProcessed || !wroteAnyImages) { | 
| 178 |                 // Delete incomplete or empty archive. | 
| 179 |                 fileTools.deleteOrWarn(targetComicFile, logger); | 
| 180 |             } | 
| 181 |             if (cause != null) { | 
| 182 |                 String message = localeTools.getMessage("errors.cannotCreateArchive", targetComicFile); | 
| 183 |   | 
| 184 |                 throw new IOExceptionWithCause(message, cause); | 
| 185 |             } | 
| 186 |         } | 
| 187 |     } | 
| 188 |   | 
| 189 |     /** | 
| 190 |      *  Add an image file to the target comic stream/document/file/whatever. | 
| 191 |      * | 
| 192 |      * @param  outImageName     the name under which the image should be stored (if applicable for | 
| 193 |      *      the output format) | 
| 194 |      * @param  sourceImageFile  the source image file to be added | 
| 195 |      */ | 
| 196 |     protected abstract void addImageFile(String outImageName, File sourceImageFile) | 
| 197 |         throws Exception; | 
| 198 |   | 
| 199 |     /** | 
| 200 |      *  Release all resources need allocated by <code>setUpComic()</code> to create the target comic | 
| 201 |      *  stream/document/file/whatever. This will be called <b>in any case</b> even if something went | 
| 202 |      *  wrong during <code>setUpComic()</code> so this routine must be able to handle <code>null</code> | 
| 203 |      *  values without resulting in <code>NullPointerExceptions</code>. | 
| 204 |      * | 
| 205 |      * @see    #setUpComic() | 
| 206 |      */ | 
| 207 |     protected abstract void cleanUpComic() | 
| 208 |         throws Exception; | 
| 209 |   | 
| 210 |     /** | 
| 211 |      *  Must be called by <code>addImageFile</code> when an image actually was added. | 
| 212 |      * | 
| 213 |      * @see    #addImageFile(String, File) | 
| 214 |      */ | 
| 215 |     protected void enableAddedAtLeastOneImage() { | 
| 216 |         wroteAnyImages = true; | 
| 217 |     } | 
| 218 |   | 
| 219 |     protected boolean hasAddedAtLeastOneImage() { | 
| 220 |         return wroteAnyImages; | 
| 221 |     } | 
| 222 | } |