Coverage Report - wjhk.jupload2.filedata.helper.ImageReaderWriterHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
ImageReaderWriterHelper
61 %
69/112
53 %
14/26
4,75
 
 1  
 //
 2  
 // $Id$
 3  
 //
 4  
 // jupload - A file upload applet.
 5  
 //
 6  
 // Copyright 2008 The JUpload Team
 7  
 //
 8  
 // Created: 12 feb. 08
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date$
 11  
 //
 12  
 // This program is free software; you can redistribute it and/or modify
 13  
 // it under the terms of the GNU General Public License as published by
 14  
 // the Free Software Foundation; either version 2 of the License, or
 15  
 // (at your option) any later version.
 16  
 //
 17  
 // This program is distributed in the hope that it will be useful,
 18  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 20  
 // GNU General Public License for more details.
 21  
 //
 22  
 // You should have received a copy of the GNU General Public License
 23  
 // along with this program; if not, write to the Free Software
 24  
 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 25  
 
 26  
 package wjhk.jupload2.filedata.helper;
 27  
 
 28  
 import java.awt.image.BufferedImage;
 29  
 import java.io.File;
 30  
 import java.io.IOException;
 31  
 import java.util.Iterator;
 32  
 
 33  
 import javax.imageio.IIOImage;
 34  
 import javax.imageio.ImageIO;
 35  
 import javax.imageio.ImageReader;
 36  
 import javax.imageio.ImageWriteParam;
 37  
 import javax.imageio.ImageWriter;
 38  
 import javax.imageio.metadata.IIOMetadata;
 39  
 import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
 40  
 import javax.imageio.stream.FileImageInputStream;
 41  
 import javax.imageio.stream.FileImageOutputStream;
 42  
 
 43  
 import wjhk.jupload2.exception.JUploadIOException;
 44  
 import wjhk.jupload2.filedata.DefaultFileData;
 45  
 import wjhk.jupload2.filedata.PictureFileData;
 46  
 import wjhk.jupload2.policies.PictureUploadPolicy;
 47  
 
 48  
 /**
 49  
  * This package provides low level methods, for picture management. It is used by {@link PictureFileData} to simplify
 50  
  * its processing.
 51  
  * 
 52  
  * @author etienne_sf
 53  
  */
 54  
 public class ImageReaderWriterHelper {
 55  
 
 56  
     /**
 57  
      * File input, from which the original picture should be read.
 58  
      */
 59  7
     FileImageInputStream fileImageInputStream = null;
 60  
 
 61  
     /**
 62  
      * File output stream for the current transformation.
 63  
      */
 64  
     FileImageOutputStream fileImageOutputStream;
 65  
 
 66  
     /**
 67  
      * The {@link PictureFileData} that this helper will have to help.
 68  
      */
 69  
     PictureFileData pictureFileData;
 70  
 
 71  
     /**
 72  
      * Current ImageReader. Initialized by {@link #initImageReader()}
 73  
      */
 74  7
     ImageReader imageReader = null;
 75  
 
 76  
     /**
 77  
      * Current ImageWriter. Initialized by {@link #initImageWriter()}
 78  
      */
 79  7
     ImageWriter imageWriter = null;
 80  
 
 81  
     /**
 82  
      * Current ImageWriter. Initialized by {@link #initImageWriter()}
 83  
      */
 84  7
     ImageWriteParam imageWriterParam = null;
 85  
 
 86  
     /**
 87  
      * Contains the target picture format (in lowercase): gif, jpg... It is used to find an ImageWriter, and to define
 88  
      * the target filename.
 89  
      */
 90  
     String targetPictureFormat;
 91  
 
 92  
     /**
 93  
      * The current upload policy must be a {@link PictureUploadPolicy}
 94  
      */
 95  
     PictureUploadPolicy uploadPolicy;
 96  
 
 97  
     // ////////////////////////////////////////////////////////////////////
 98  
     // //////////////////// CONSTRUCTOR
 99  
     // ////////////////////////////////////////////////////////////////////
 100  
 
 101  
     /**
 102  
      * Standard constructor.
 103  
      * 
 104  
      * @param uploadPolicy The current upload policy.
 105  
      * @param pictureFileData The file data to be 'helped'.
 106  
      */
 107  7
     public ImageReaderWriterHelper(PictureUploadPolicy uploadPolicy, PictureFileData pictureFileData) {
 108  7
         this.uploadPolicy = uploadPolicy;
 109  7
         this.pictureFileData = pictureFileData;
 110  
 
 111  14
         this.targetPictureFormat = uploadPolicy.getImageFileConversionInfo().getTargetFormat(
 112  7
                 pictureFileData.getFileExtension());
 113  7
     }
 114  
 
 115  
     // ////////////////////////////////////////////////////////////////////
 116  
     // //////////////////// PUBLIC METHODS
 117  
     // ////////////////////////////////////////////////////////////////////
 118  
 
 119  
     /**
 120  
      * returns the target picture format (lowercase, may be the same as the file extension)
 121  
      * 
 122  
      * @return the target picture format (lowercase, may be the same as the file extension)
 123  
      */
 124  
     public String getTargetPictureFormat() {
 125  5
         return this.targetPictureFormat;
 126  
     }
 127  
 
 128  
     /**
 129  
      * Creates a FileImageOutputStream, and assign it as the output to the imageWriter.
 130  
      * 
 131  
      * @param file The file where the output stream must write.
 132  
      * @throws JUploadIOException Any error...
 133  
      */
 134  
     public void setOutput(File file) throws JUploadIOException {
 135  
         // We first initialize internal variable.
 136  5
         initImageWriter();
 137  
 
 138  
         try {
 139  5
             this.fileImageOutputStream = new FileImageOutputStream(file);
 140  0
         } catch (IOException e) {
 141  0
             throw new JUploadIOException("ImageReaderWriterHelper.setOutput()", e);
 142  5
         }
 143  5
         this.imageWriter.setOutput(this.fileImageOutputStream);
 144  5
     }
 145  
 
 146  
     /**
 147  
      * Free all reserved resource by this helper. Includes closing of any input or output stream.
 148  
      * 
 149  
      * @throws JUploadIOException Any IO Exception
 150  
      */
 151  
     public void dispose() throws JUploadIOException {
 152  
         // First: let's free any resource used by ImageWriter.
 153  7
         if (this.imageWriter != null) {
 154  
             // An imageWriter was initialized. Let's free it.
 155  5
             this.imageWriter.dispose();
 156  5
             if (this.fileImageOutputStream != null) {
 157  
                 try {
 158  5
                     this.fileImageOutputStream.close();
 159  0
                 } catch (IOException e) {
 160  0
                     throw new JUploadIOException("ImageReaderWriterHelper.dispose() [fileImageOutputStream]", e);
 161  5
                 }
 162  
             }
 163  5
             this.imageWriter = null;
 164  5
             this.fileImageOutputStream = null;
 165  
         }
 166  
 
 167  
         // Then, all about ImageReader
 168  7
         if (this.imageReader != null) {
 169  
             // An imageReader was initialized. Let's free it.
 170  7
             this.imageReader.dispose();
 171  
             try {
 172  7
                 this.fileImageInputStream.close();
 173  0
             } catch (IOException e) {
 174  0
                 throw new JUploadIOException("ImageReaderWriterHelper.dispose() [fileImageInputStream]", e);
 175  7
             }
 176  7
             this.imageReader = null;
 177  7
             this.fileImageInputStream = null;
 178  
         }
 179  7
     }
 180  
 
 181  
     /**
 182  
      * Call to imageReader.getNumImages(boolean). Caution: may be long, for big files.
 183  
      * 
 184  
      * @param allowSearch
 185  
      * @return The number of image into this file.
 186  
      * @throws JUploadIOException
 187  
      */
 188  
     public int getNumImages(boolean allowSearch) throws JUploadIOException {
 189  0
         initImageReader();
 190  
         try {
 191  0
             return this.imageReader.getNumImages(allowSearch);
 192  0
         } catch (IOException e) {
 193  0
             throw new JUploadIOException("ImageReaderWriterHelper.getNumImages() [fileImageInputStream]", e);
 194  
         }
 195  
     }
 196  
 
 197  
     /**
 198  
      * Call to ImageIO.read(fileImageInputStream).
 199  
      * 
 200  
      * @return The BufferedImage read
 201  
      * @throws JUploadIOException public BufferedImage imageIORead() throws JUploadIOException { try { return
 202  
      *             ImageIO.read(this.pictureFileData.getWorkingSourceFile()); } catch (IOException e) { throw new
 203  
      *             JUploadIOException( "ImageReaderWriterHelper.ImageIORead()", e); } }
 204  
      */
 205  
 
 206  
     /**
 207  
      * Read an image, from the pictureFileData.
 208  
      * 
 209  
      * @param imageIndex The index number of the picture, in the file. 0 for the first picture (only valid value for
 210  
      *            picture containing one picture)
 211  
      * @return The image corresponding to this index, in the picture file.
 212  
      * @throws JUploadIOException
 213  
      * @throws IndexOutOfBoundsException Occurs when the imageIndex is wrong.
 214  
      */
 215  
     public BufferedImage readImage(int imageIndex) throws JUploadIOException, IndexOutOfBoundsException {
 216  7
         initImageReader();
 217  
         try {
 218  14
             this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading picture number " + imageIndex
 219  7
                     + " of file " + this.pictureFileData.getFileName(), 30);
 220  7
             return this.imageReader.read(imageIndex);
 221  0
         } catch (IndexOutOfBoundsException e) {
 222  
             // The IndexOutOfBoundsException is transmitted to the caller. It
 223  
             // indicates that the index is out of bound. It's the good way for
 224  
             // the caller to stop the loop through available pictures.
 225  0
             throw e;
 226  0
         } catch (IOException e) {
 227  0
             throw new JUploadIOException("ImageReaderWriterHelper.readImage(" + imageIndex + ")", e);
 228  
         }
 229  
     }
 230  
 
 231  
     /**
 232  
      * Read an image, from the pictureFileData.
 233  
      * 
 234  
      * @param imageIndex The index number of the picture, in the file. 0 for the first picture (only valid value for
 235  
      *            picture containing one picture)
 236  
      * @return The full image data for this index
 237  
      * @throws JUploadIOException
 238  
      * @throws IndexOutOfBoundsException Occurs when the imageIndex is wrong.
 239  
      */
 240  
     public IIOImage readAll(int imageIndex) throws JUploadIOException, IndexOutOfBoundsException {
 241  0
         initImageReader();
 242  
         try {
 243  0
             this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading picture number " + imageIndex
 244  0
                     + " of file " + this.pictureFileData.getFileName(), 30);
 245  0
             return this.imageReader.readAll(imageIndex, this.imageReader.getDefaultReadParam());
 246  0
         } catch (IndexOutOfBoundsException e) {
 247  
             // The IndexOutOfBoundsException is transmitted to the caller. It
 248  
             // indicates that the index is out of bound. It's the good way for
 249  
             // the caller to stop the loop through available pictures.
 250  0
             throw e;
 251  0
         } catch (IOException e) {
 252  0
             throw new JUploadIOException("ImageReaderWriterHelper.readAll(" + imageIndex + ")", e);
 253  
         }
 254  
     }
 255  
 
 256  
     /**
 257  
      * Load the metadata associated with one picture in the picture file.
 258  
      * 
 259  
      * @param imageIndex
 260  
      * @return The metadata loaded
 261  
      * @throws JUploadIOException Any IOException is encapsulated in this exception
 262  
      */
 263  
     public IIOMetadata getImageMetadata(int imageIndex) throws JUploadIOException {
 264  
         // We must have the reader initialized
 265  5
         initImageReader();
 266  
 
 267  
         // Ok, let's go
 268  
         try {
 269  10
             this.uploadPolicy.displayDebug("ImageReaderWriterHelper: reading metadata for picture number " + imageIndex
 270  5
                     + " of file " + this.pictureFileData.getFileName(), 30);
 271  5
             return this.imageReader.getImageMetadata(imageIndex);
 272  0
         } catch (IOException e) {
 273  0
             throw new JUploadIOException("ImageReaderWriterHelper.getImageMetadata()", e);
 274  
         }
 275  
     }
 276  
 
 277  
     /**
 278  
      * Write a picture in the output picture file. Called just before an upload.
 279  
      * 
 280  
      * @param numIndex The index of the image in the transformed picture file.
 281  
      * @param iioImage The image to write.
 282  
      * @param iwp The parameter to use to write this image.
 283  
      * @throws JUploadIOException
 284  
      */
 285  
     public void writeInsert(int numIndex, IIOImage iioImage, ImageWriteParam iwp) throws JUploadIOException {
 286  0
         initImageWriter();
 287  
         try {
 288  0
             this.imageWriter.writeInsert(numIndex, iioImage, iwp);
 289  0
         } catch (IOException e) {
 290  0
             throw new JUploadIOException("ImageReaderWriterHelper.writeInsert()", e);
 291  0
         }
 292  0
     }
 293  
 
 294  
     /**
 295  
      * Write a picture in the output picture file. Called just before an upload.
 296  
      * 
 297  
      * @param iioImage The image to write.
 298  
      * @throws JUploadIOException
 299  
      */
 300  
     public void write(IIOImage iioImage) throws JUploadIOException {
 301  5
         initImageWriter();
 302  
         try {
 303  5
             this.imageWriter.write(null, iioImage, this.imageWriterParam);
 304  0
         } catch (IOException e) {
 305  0
             throw new JUploadIOException("ImageReaderWriterHelper.write()", e);
 306  5
         }
 307  5
     }
 308  
 
 309  
     // ////////////////////////////////////////////////////////////////////
 310  
     // //////////////////// PRIVATE METHODS
 311  
     // ////////////////////////////////////////////////////////////////////
 312  
 
 313  
     /**
 314  
      * Initialize the ImageWriter and the ImageWriteParam for the current picture helper.
 315  
      * 
 316  
      * @throws JUploadIOException
 317  
      */
 318  
     private void initImageWriter() throws JUploadIOException {
 319  10
         if (this.imageWriter == null) {
 320  
             // Get the writer (to choose the compression quality)
 321  
             // In the windows world, file extension may be in uppercase, which
 322  
             // is not compatible with the core Java API.
 323  5
             Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(this.targetPictureFormat);
 324  5
             if (!iter.hasNext()) {
 325  
                 // Too bad: no writer for the selected picture format
 326  
 
 327  
                 // A particular case: no gif support in JRE 1.5. A JRE upgrade
 328  
                 // must be done.
 329  0
                 if (this.targetPictureFormat.equals("gif") && System.getProperty("java.version").startsWith("1.5")) {
 330  0
                     throw new JUploadIOException(
 331  
                             "gif pictures are not supported in Java 1.5. Please switch to JRE 1.6.");
 332  
                 }
 333  
 
 334  0
                 throw new JUploadIOException("No writer for the '" + this.targetPictureFormat + "' picture format.");
 335  
             } else {
 336  5
                 this.imageWriter = iter.next();
 337  5
                 this.imageWriterParam = this.imageWriter.getDefaultWriteParam();
 338  
 
 339  
                 // For jpeg pictures, we force the compression level.
 340  5
                 if (this.targetPictureFormat.equalsIgnoreCase("jpg")
 341  5
                         || this.targetPictureFormat.equalsIgnoreCase("jpeg")) {
 342  0
                     this.imageWriterParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
 343  
                     // Let's select a good compromise between picture size
 344  
                     // and quality.
 345  0
                     this.imageWriterParam.setCompressionQuality(this.uploadPolicy.getPictureCompressionQuality());
 346  
                     // In some case, we need to force the Huffman tables:
 347  0
                     ((JPEGImageWriteParam) this.imageWriterParam).setOptimizeHuffmanTables(true);
 348  
                 }
 349  
 
 350  
                 //
 351  
                 try {
 352  5
                     this.uploadPolicy.displayDebug(
 353  5
                             "ImageWriter1 (used), CompressionQuality=" + this.imageWriterParam.getCompressionQuality(),
 354  
                             50);
 355  5
                 } catch (Exception e2) {
 356  
                     // If we come here, compression is not supported for
 357  
                     // this picture format, or parameters are not explicit
 358  
                     // mode, or ... (etc). May trigger several different
 359  
                     // errors. We just ignore them: this par of code is only
 360  
                     // to write some debug info.
 361  5
                     this.uploadPolicy.displayWarn(e2.getClass().getName() + " in ImageReaderWriterHelper.java");
 362  0
                 }
 363  
             }
 364  
         }
 365  10
     }// initImageWriter
 366  
 
 367  
     /**
 368  
      * Initialize the ImageReader for the current helper.
 369  
      * 
 370  
      * @throws JUploadIOException
 371  
      */
 372  
     private void initImageReader() throws JUploadIOException {
 373  
         // First: we open a ImageInputStream
 374  
         try {
 375  12
             this.fileImageInputStream = new FileImageInputStream(this.pictureFileData.getWorkingSourceFile());
 376  0
         } catch (IOException e) {
 377  0
             throw new JUploadIOException("ImageReaderWriterHelper.initImageReader()", e);
 378  12
         }
 379  
 
 380  
         // Then: we create an ImageReader, and assign the ImageInputStream to
 381  
         // it.
 382  12
         if (this.imageReader == null) {
 383  7
             String ext = DefaultFileData.getExtension(this.pictureFileData.getFileName());
 384  7
             Iterator<ImageReader> iterator = ImageIO.getImageReadersBySuffix(ext);
 385  7
             if (iterator.hasNext()) {
 386  7
                 this.imageReader = iterator.next();
 387  7
                 this.imageReader.setInput(this.fileImageInputStream);
 388  7
                 this.uploadPolicy.displayDebug("Foud one reader for " + ext + " extension", 50);
 389  
             }// while
 390  
 
 391  
             // Did we find our reader ?
 392  7
             if (this.imageReader == null) {
 393  0
                 this.uploadPolicy.displayErr("Found no reader for " + ext + " extension");
 394  7
             } else if (this.uploadPolicy.getDebugLevel() >= 50) {
 395  
                 // This call may be long, so we do it only if useful.
 396  
                 try {
 397  14
                     this.uploadPolicy.displayDebug("Nb images in " + this.pictureFileData.getFileName() + ": "
 398  7
                             + this.imageReader.getNumImages(true), 50);
 399  0
                 } catch (IOException e) {
 400  
                     // We mask the error, was just for debug...
 401  7
                 }
 402  
             }
 403  
         }
 404  12
     }
 405  
 }