source: 3thparty/jupload/src/main/java/wjhk/jupload2/filedata/PictureFileData.java @ 3951

Revision 3951, 31.4 KB checked in by alexandrecorreia, 13 years ago (diff)

Ticket #1709 - Adicao de codigo fonte java do componente jupload

Line 
1//
2// $Id: PictureFileData.java 287 2007-06-17 09:07:04 +0000 (dim., 17 juin 2007)
3// felfert $
4//
5// jupload - A file upload applet.
6// Copyright 2007 The JUpload Team
7//
8// Created: 2006-05-09
9// Creator: etienne_sf
10// Last modified: $Date: 2010-06-21 10:05:43 -0300 (Seg, 21 Jun 2010) $
11//
12// This program is free software; you can redistribute it and/or modify it under
13// the terms of the GNU General Public License as published by the Free Software
14// Foundation; either version 2 of the License, or (at your option) any later
15// version. This program is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18// details. You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software Foundation, Inc.,
20// 675 Mass Ave, Cambridge, MA 02139, USA.
21
22package wjhk.jupload2.filedata;
23
24import java.awt.Canvas;
25import java.awt.Image;
26import java.awt.image.BufferedImage;
27import java.io.File;
28import java.io.FileInputStream;
29import java.io.FileNotFoundException;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.util.Iterator;
34
35import javax.imageio.IIOImage;
36import javax.imageio.ImageIO;
37import javax.imageio.ImageReader;
38import javax.imageio.metadata.IIOMetadata;
39import javax.imageio.stream.FileImageInputStream;
40import javax.swing.ImageIcon;
41import javax.swing.JOptionPane;
42
43import wjhk.jupload2.exception.JUploadException;
44import wjhk.jupload2.exception.JUploadIOException;
45import wjhk.jupload2.filedata.helper.ImageHelper;
46import wjhk.jupload2.filedata.helper.ImageReaderWriterHelper;
47import wjhk.jupload2.policies.PictureUploadPolicy;
48import wjhk.jupload2.policies.UploadPolicy;
49
50/**
51 * This class contains all data about files to upload as a picture. It adds the
52 * following elements to the {@link wjhk.jupload2.filedata.FileData} class :<BR>
53 * <UL>
54 * <LI>Ability to define a target format (to convert pictures to JPG before
55 * upload, for instance)
56 * <LI>Optional definition of a maximal width and/or height.
57 * <LI>Ability to rotate a picture, with {@link #addRotation(int)}
58 * <LI>Ability to store a picture into a BufferedImage. This is actualy a bad
59 * idea within an applet (should run within a java application) : the applet
60 * runs very quickly out of memory. With pictures from my Canon EOS20D (3,5M), I
61 * can only display two pictures. The third one generates an out of memory
62 * error, despite the System.finalize and System.gc I've put everywhere in the
63 * code!
64 * </UL>
65 *
66 * @author etienne_sf
67 * @version $Revision: 1355 $
68 */
69public class PictureFileData extends DefaultFileData {
70
71        /**
72         * Indicates if this file is a picture or not. This is bases on the return
73         * of ImageIO.getImageReadersByFormatName().
74         */
75        boolean isPicture = false;
76
77        /**
78         * This picture is precalculated, and stored to avoid to calculate it each
79         * time the user select this picture again, or each time the use switch from
80         * an application to another.
81         */
82        Image offscreenImage = null;
83
84        /**
85         * quarterRotation contains the current rotation that will be applied to the
86         * picture. Its value should be one of 0, 1, 2, 3. It is controled by the
87         * {@link #addRotation(int)} method.
88         * <UL>
89         * <LI>0 means no rotation.
90         * <LI>1 means a rotation of 90ï¿œ clockwise (word = Ok ??).
91         * <LI>2 means a rotation of 180ï¿œ.
92         * <LI>3 means a rotation of 900 counterclockwise (word = Ok ??).
93         * </UL>
94         */
95        int quarterRotation = 0;
96
97        /**
98         * Width of the original picture. The width is taken from the first image of
99         * the file. We expect that all pictures in a file are of the same size (for
100         * instance, for animated gif). Calculated in the
101         * {@link #PictureFileData(File, File, PictureUploadPolicy)} constructor.
102         */
103        int originalWidth = -1;
104
105        /**
106         * Same as {@link #originalWidth}, for the height of the first image in the
107         * picture file.
108         */
109        int originalHeight = -1;
110
111        /**
112         * transformedPictureFile contains the reference to the temporary file that
113         * stored the transformed picture, during upload. It is created by
114         * {@link #getInputStream()} and freed by {@link #afterUpload()}.
115         */
116        File transformedPictureFile = null;
117
118        /**
119         * uploadLength contains the uploadLength, which is : <BR>
120         * - The size of the original file, if no transformation is needed. <BR>
121         * - The size of the transformed file, if a transformation were made. <BR>
122         * <BR>
123         * It is set to -1 whenever its calculation is to be done again (for
124         * instance, when the user ask for a rotation, which is currently the only
125         * action that need to recalculate the picture).
126         */
127        long uploadLength = -1;
128
129        /**
130         * Contains the reference to a copy of the original picture files.
131         * Originally created because a SUN bug would prevent picture to be
132         * correctly resized if the original picture filename contains accents (or
133         * any non-ASCII characters).
134         */
135
136        File workingCopyTempFile = null;
137
138        /**
139         * will be set if in {@link #createTranformedPictureFile(ImageHelper)}, if
140         * an image-transformation has occured
141         */
142        String targetPictureFormat;
143
144        /**
145         * Standard constructor: needs a PictureFileDataPolicy.
146         *
147         * @param file
148         *            The files which data are to be handled by this instance.
149         * @param root
150         *            The root directory, to calculate the relative dir (see
151         *            {@link #getRelativeDir()}.
152         * @param uploadPolicy
153         *            The current upload policy
154         * @throws JUploadIOException
155         *             Encapsulation of the IOException, if any would occurs.
156         */
157        public PictureFileData(File file, File root,
158                        PictureUploadPolicy uploadPolicy) throws JUploadIOException {
159                super(file, root, uploadPolicy);
160
161                String fileExtension = getFileExtension();
162
163                // Is it a picture?
164                this.uploadPolicy.displayDebug("Looking for iterator of extension '"
165                                + file + "'", 80);
166                this.isPicture = isFileAPicture(file);
167
168                // Let's log the test result
169                this.uploadPolicy.displayDebug("isPicture=" + this.isPicture + " ("
170                                + file.getName() + "), extension=" + fileExtension, 50);
171
172                // If it's a picture, we override the default mime type:
173                if (this.isPicture) {
174                        setMimeTypeByExtension(fileExtension);
175                }
176        }
177
178        /**
179         * Free any available memory. This method is called very often here, to be
180         * sure that we don't use too much memory. But we still run out of memory in
181         * some case.
182         *
183         * @param caller
184         *            Indicate the method or treatment from which this method is
185         *            called.
186         * @param uploadPolicy
187         *            The current upload policy is not available, to this static
188         *            method...
189         */
190        public static void freeMemory(String caller, UploadPolicy uploadPolicy) {
191                Runtime rt = Runtime.getRuntime();
192
193                rt.runFinalization();
194                rt.gc();
195
196                if (uploadPolicy.getDebugLevel() >= 50) {
197                        uploadPolicy.displayDebug("freeMemory (after " + caller + ") : "
198                                        + rt.freeMemory() + " (maxMemory: " + rt.maxMemory()
199                                        + ", totalMemory: " + rt.totalMemory() + ")", 50);
200                }
201        }
202
203        /**
204         * If this pictures needs transformation, a temporary file is created. This
205         * can occurs if the original picture is bigger than the maxWidth or
206         * maxHeight, of if it has to be rotated. This temporary file contains the
207         * transformed picture. <BR>
208         * The call to this method is optional, if the caller calls
209         * {@link #getUploadLength()}. This method calls beforeUpload() if the
210         * uploadLength is unknown.
211         */
212        @Override
213        public void beforeUpload() throws JUploadException {
214                this.uploadPolicy.displayDebug(this.hashCode()
215                                + "|Entering PictureFileData.beforeUpload()", 95);
216
217                if (!this.preparedForUpload) {
218                        if (this.uploadLength < 0) {
219                                try {
220                                        // Picture management is a big work. Let's try to free some
221                                        // memory.
222                                        freeMemory(
223                                                        "Picture manabeforeUpload(): before initTransformedPictureFile",
224                                                        this.uploadPolicy);
225
226                                        // Get the transformed picture file, if needed.
227                                        initTransformedPictureFile();
228
229                                        // More debug output, to understand where the applet
230                                        // freezes.
231                                        this.uploadPolicy
232                                                        .displayDebug(
233                                                                        this.getClass().getName()
234                                                                                        + ".beforeUpload(): after call to initTransformedPictureFile()",
235                                                                        100);
236
237                                } catch (OutOfMemoryError e) {
238                                        // Oups ! My EOS 20D has too big pictures to handle more
239                                        // than
240                                        // two pictures in a navigator applet !!!!!
241                                        // :-(
242                                        //
243                                        // We don't transform it. We clean the file, if it has been
244                                        // created.
245                                        // More debug output, to understand where the applet
246                                        // freezes.
247                                        this.uploadPolicy.displayDebug(this.getClass().getName()
248                                                        + ".beforeUpload(): OutOfMemoryError", 30);
249                                        deleteTransformedPictureFile();
250                                        deleteWorkingCopyPictureFile();
251
252                                        // Let's try to free some memory.
253                                        freeMemory("beforeUpload(): in OutOfMemoryError",
254                                                        this.uploadPolicy);
255
256                                        tooBigPicture();
257                                }
258
259                                // If the transformed picture is correctly created, we'll upload
260                                // it.
261                                // Else we upload the original file.
262                                synchronized (this) {
263                                        if (this.transformedPictureFile != null) {
264                                                this.uploadLength = this.transformedPictureFile
265                                                                .length();
266                                                setMimeTypeByExtension(this.targetPictureFormat);
267                                        } else {
268                                                this.uploadLength = getFile().length();
269                                        }
270                                }
271                        }
272                }
273
274                // Let's check that everything is Ok
275                // More debug output, to understand where the applet freezes.
276                this.uploadPolicy.displayDebug(this.getClass().getName()
277                                + ".beforeUpload(): before call to super.beforeUpload()", 100);
278
279                super.beforeUpload();
280
281                // Let's check that everything is Ok
282                // More debug output, to understand where the applet freezes.
283                this.uploadPolicy.displayDebug(this.getClass().getName()
284                                + ".beforeUpload(): after call to super.beforeUpload()", 100);
285        }
286
287        /**
288         * Returns the number of bytes, for this upload. If needed, that is, if
289         * uploadlength is unknown, {@link #beforeUpload()} is called.
290         *
291         * @return The length of upload. In this class, this is ... the size of the
292         *         original file, or the transformed file!
293         */
294        @Override
295        public long getUploadLength() {
296                // Just for debug, to be removed before release.
297
298                if (!this.preparedForUpload) {
299                        throw new IllegalStateException("The file " + getFileName()
300                                        + " is not prepared for upload");
301                }
302                return this.uploadLength;
303        }
304
305        /**
306         * This function create an input stream for this file. The caller is
307         * responsible for closing this input stream. <BR>
308         * This function assumes that the {@link #getUploadLength()} method has
309         * already be called : it is responsible for creating the temporary file (if
310         * needed). If not called, the original file will be sent.
311         *
312         * @return An inputStream
313         */
314        @Override
315        public synchronized InputStream getInputStream() throws JUploadException {
316                this.uploadPolicy.displayDebug(this.hashCode()
317                                + "|Entering PictureFileData.getInputStream()", 95);
318                if (!this.preparedForUpload) {
319                        throw new IllegalStateException("The file " + getFileName()
320                                        + " is not prepared for upload");
321                }
322                // Do we have to transform the picture ?
323                if (this.transformedPictureFile != null) {
324                        try {
325                                return new FileInputStream(this.transformedPictureFile);
326                        } catch (FileNotFoundException e) {
327                                throw new JUploadIOException(e);
328                        }
329                }
330                // Otherwise : we read the file, in the standard way.
331                return super.getInputStream();
332        }
333
334        /**
335         * Cleaning of the temporary file on the hard drive, if any. <BR>
336         * <B>Note:</B> if the debugLevel is 100 (or more) this temporary file is
337         * not removed. This allow control of this created file.
338         */
339        @Override
340        public void afterUpload() {
341                // Free the temporary file ... if any.
342                deleteTransformedPictureFile();
343                deleteWorkingCopyPictureFile();
344                this.uploadLength = -1;
345
346                super.afterUpload();
347        }
348
349        /**
350         * This method creates a new Image, from the current picture. The resulting
351         * width and height will be less or equal than the given maximum width and
352         * height. The scale is maintained. Thus the width or height may be inferior
353         * than the given values.
354         *
355         * @param canvas
356         *            The canvas on which the picture will be displayed.
357         * @param shadow
358         *            True if the pictureFileData should store this picture. False
359         *            if the pictureFileData instance should not store this picture.
360         *            Store this picture avoid calculating the image each time the
361         *            user selects it in the file panel.
362         * @return The rescaled image.
363         * @throws JUploadException
364         *             Encapsulation of the Exception, if any would occurs.
365         */
366        public Image getImage(Canvas canvas, boolean shadow)
367                        throws JUploadException {
368                Image localImage = null;
369
370                // ////////////////////////////////////////////////////////////////////////
371                // ////////////// Some preliminary tests.
372                // ////////////////////////////////////////////////////////////////////////
373
374                if (canvas == null) {
375                        throw new JUploadException(
376                                        "canvas null in PictureFileData.getImage");
377                }
378
379                if (shadow && this.offscreenImage != null) {
380                        return this.offscreenImage;
381                }
382
383                int canvasWidth = canvas.getWidth();
384                int canvasHeight = canvas.getHeight();
385                if (canvasWidth <= 0 || canvasHeight <= 0) {
386                        this.uploadPolicy
387                                        .displayDebug(
388                                                        "canvas width and/or height null in PictureFileData.getImage()",
389                                                        1);
390                        return null;
391                }
392
393                if (!this.isPicture) {
394                        this.uploadPolicy
395                                        .displayWarn("canvas width and/or height null in PictureFileData.getImage(). PictureFileData.getImage will return null");
396                        return null;
397                }
398                // ////////////////////////////////////////////////////////////////////////
399                // ////////////// End of preliminary tests: let's work.
400                // ////////////////////////////////////////////////////////////////////////
401
402                try {
403                        // First: load the picture.
404                        ImageReaderWriterHelper irwh = new ImageReaderWriterHelper(
405                                        (PictureUploadPolicy) this.uploadPolicy, this);
406                        BufferedImage sourceImage = irwh.readImage(0);
407                        irwh.dispose();
408                        irwh = null;
409                        ImageHelper ih = new ImageHelper(
410                                        (PictureUploadPolicy) this.uploadPolicy, this, canvasWidth,
411                                        canvasHeight, this.quarterRotation);
412                        localImage = ih.getBufferedImage(
413                                        ((PictureUploadPolicy) this.uploadPolicy)
414                                                        .getHighQualityPreview(), sourceImage);
415                        // We free memory ASAP.
416                        sourceImage.flush();
417                        sourceImage = null;
418                } catch (OutOfMemoryError e) {
419                        // Too bad
420                        localImage = null;
421                        tooBigPicture();
422                }
423
424                // We store it, if asked to.
425                if (shadow) {
426                        this.offscreenImage = localImage;
427                }
428
429                freeMemory("end of " + this.getClass().getName() + ".getImage()",
430                                this.uploadPolicy);
431
432                // The picture is now loaded. We clear the progressBar
433                this.uploadPolicy.getContext().getUploadPanel()
434                                .getPreparationProgressBar().setValue(0);
435
436                return localImage;
437        }// getImage
438
439        /**
440         * This function is used to rotate the picture. The current rotation state
441         * is kept in the quarterRotation private attribute.
442         *
443         * @param quarter
444         *            Number of quarters (90 degrees) the picture should rotate. 1
445         *            means rotating of 90 degrees clockwise. Can be negative.
446         */
447        public void addRotation(int quarter) {
448                this.quarterRotation += quarter;
449
450                // We'll have to recalculate the upload length, as the resulting file is
451                // different.
452                // If any file has been prepared, they must be deleted
453                deleteWorkingCopyPictureFile();
454                deleteTransformedPictureFile();
455                this.uploadLength = -1;
456
457                // We keep the 'quarter' in the segment [0;4[
458                while (this.quarterRotation < 0) {
459                        this.quarterRotation += 4;
460                }
461                while (this.quarterRotation >= 4) {
462                        this.quarterRotation -= 4;
463                }
464
465                // We need to change the precalculated picture, if any
466                if (this.offscreenImage != null) {
467                        this.offscreenImage.flush();
468                        this.offscreenImage = null;
469                }
470        }
471
472        /**
473         * Indicates if this file is actually a picture or not.
474         *
475         * @return the isPicture flag.
476         */
477        public boolean isPicture() {
478                return this.isPicture;
479        }
480
481        /** @see FileData#getMimeType() */
482        @Override
483        public String getMimeType() {
484                return this.mimeType;
485        }
486
487        // ///////////////////////////////////////////////////////////////////////////////////////////
488        // /////////////////////////// private METHODS
489        // ///////////////////////////////////////////////////////////////////////////////////////////
490
491        /**
492         * File.deleteOnExit() is pretty unreliable, especially in applets.
493         * Therefore the applet provides a callback which is executed during applet
494         * termination. This method performs the actual cleanup.
495         */
496        public synchronized void deleteTransformedPictureFile() {
497                this.uploadPolicy.displayDebug(this.hashCode()
498                                + "|Entering PictureFileData.deleteTransformedPictureFile()",
499                                95);
500                // Free the temporary file ... if any.
501                if (null != this.transformedPictureFile
502                                && this.uploadPolicy.getDebugLevel() <= 100) {
503                        if (!this.transformedPictureFile.delete()) {
504                                this.uploadPolicy.displayWarn("Unable to delete "
505                                                + this.transformedPictureFile.getName());
506                        }
507                        this.transformedPictureFile = null;
508
509                }
510        }
511
512        /**
513         * Creation of a temporary file, that contains the transformed picture. For
514         * instance, it can be resized or rotated. This method doesn't throw
515         * exception when there is an IOException within its procedure. If an
516         * exception occurs while building the temporary file, the exception is
517         * caught, a warning is displayed, the temporary file is deleted (if it was
518         * created), and the upload will go on with the original file. <BR>
519         * Note: any JUploadException thrown by a method called within
520         * getTransformedPictureFile() will be thrown within this method.
521         */
522        void initTransformedPictureFile() throws JUploadException {
523                this.uploadPolicy.displayDebug(this.hashCode()
524                                + "|Entering PictureFileData.initTransformedPictureFile()", 95);
525                int targetMaxWidth;
526                int targetMaxHeight;
527
528                // If the image is rotated, we compare to realMaxWidth and
529                // realMaxHeight, instead of maxWidth and maxHeight. This allows
530                // to have a different picture size for rotated and not rotated
531                // pictures. See the UploadPolicy javadoc for details ... and a
532                // good reason ! ;-)
533                if (this.quarterRotation == 0) {
534                        targetMaxWidth = ((PictureUploadPolicy) this.uploadPolicy)
535                                        .getMaxWidth();
536                        targetMaxHeight = ((PictureUploadPolicy) this.uploadPolicy)
537                                        .getMaxHeight();
538                } else {
539                        targetMaxWidth = ((PictureUploadPolicy) this.uploadPolicy)
540                                        .getRealMaxWidth();
541                        targetMaxHeight = ((PictureUploadPolicy) this.uploadPolicy)
542                                        .getRealMaxHeight();
543                }
544
545                // Some Helper will .. help us !
546                // I like useful comment :-)
547                ImageHelper imageHelper = new ImageHelper(
548                                (PictureUploadPolicy) this.uploadPolicy, this, targetMaxWidth,
549                                targetMaxHeight, this.quarterRotation);
550
551                // Should transform the file, and do we already created the transformed
552                // file ?
553                synchronized (this) {
554                        if (imageHelper.hasToTransformPicture()
555                                        && this.transformedPictureFile == null) {
556
557                                // We have to create a resized or rotated picture file, and all
558                                // needed information.
559                                // ...let's do it
560                                try {
561                                        createTranformedPictureFile(imageHelper);
562                                } catch (JUploadException e) {
563                                        // Hum, too bad.
564                                        // if any file was created, we remove it.
565                                        deleteTransformedPictureFile();
566                                        throw e;
567                                }
568                        }
569                }
570        }// end of initTransformedPictureFile
571
572        /**
573         * Creates a transformed picture file of the given max width and max height.
574         * If the {@link #transformedPictureFile} attribute is not set before
575         * calling this method, it will be set. If set before, the existing
576         * {@link #transformedPictureFile} is replaced by the newly transformed
577         * picture file. It is cleared if an error occured. <BR>
578         *
579         * @param imageHelper
580         *            The {@link ImageHelper} that was initialized with current
581         *            parameters.
582         */
583        synchronized void createTranformedPictureFile(ImageHelper imageHelper)
584                        throws JUploadException {
585                this.uploadPolicy.displayDebug(this.hashCode()
586                                + "|Entering PictureFileData.createTransformedPictureFile()",
587                                95);
588
589                IIOMetadata metadata = null;
590                IIOImage iioImage = null;
591                BufferedImage originalImage = null;
592                BufferedImage transformedImage = null;
593                ImageReaderWriterHelper imageWriterHelper = new ImageReaderWriterHelper(
594                                (PictureUploadPolicy) this.uploadPolicy, this);
595                boolean transmitMetadata = ((PictureUploadPolicy) this.uploadPolicy)
596                                .getPictureTransmitMetadata();
597
598                // Creation of the transformed picture file.
599                createTransformedTempFile();
600                this.targetPictureFormat = imageWriterHelper.getTargetPictureFormat();
601                imageWriterHelper.setOutput(this.transformedPictureFile);
602
603                // How many picture should we read from the input file.
604                // Default number of pictures is one.
605                int nbPictures = 1;
606                // For gif file, we put a max to MAX_VALUE, and we check the
607                // IndexOutOfBoundsException to identify when we've read all pictures
608                if (getExtension(getFile()).equalsIgnoreCase("gif")) {
609                        nbPictures = Integer.MAX_VALUE;
610                }
611                this.uploadPolicy.displayDebug(
612                                "Reading image with imageWriterHelper.readImage(i)", 50);
613                // Now, we have to read each picture from the original file, apply
614                // the calculated transformation, and write each transformed picture
615                // to the writer.
616                // As indicated in javadoc for ImageReader.getNumImages(), we go
617                // through pictures, until we get an IndexOutOfBoundsException.
618                try {
619                        for (int i = 0; i < nbPictures; i += 1) {
620                                originalImage = imageWriterHelper.readImage(i);
621                                transformedImage = imageHelper.getBufferedImage(true,
622                                                originalImage);
623
624                                // If necessary, we load the metadata for the current
625                                // picture
626                                if (transmitMetadata) {
627                                        metadata = imageWriterHelper.getImageMetadata(i);
628                                }
629
630                                iioImage = new IIOImage(transformedImage, null, metadata);
631                                imageWriterHelper.write(iioImage);
632
633                                // Let's clear picture, to force getBufferedImage to read a new
634                                // one,
635                                // in the next loop.
636                                if (originalImage != null) {
637                                        originalImage.flush();
638                                        originalImage = null;
639                                }
640                        }// for
641                } catch (IndexOutOfBoundsException e) {
642                        // Was sent by imageWriterHelper.readImage(i)
643                        // Ok, no more picture to read. We just want to go out of
644                        // the loop. No error.
645                        this.uploadPolicy.displayDebug(
646                                        "IndexOutOfBoundsException catched: end of reading for file "
647                                                        + getFileName(), 10);
648                }
649
650                if (originalImage != null) {
651                        originalImage.flush();
652                        originalImage = null;
653                }
654
655                // Let's free any used resource.
656                imageWriterHelper.dispose();
657
658        }
659
660        /**
661         * This method is called when an OutOfMemoryError occurs. This can easily
662         * happen within the navigator, with big pictures: I've put a lot of
663         * freeMemory calls within the code, but they don't seem to work very well.
664         * When running from eclipse, the memory is freed Ok !
665         */
666        void tooBigPicture() {
667                String msg = this.uploadPolicy.getLocalizedString("tooBigPicture",
668                                getFileName());
669                this.uploadPolicy.displayWarn(msg);
670                JOptionPane.showMessageDialog(null, msg, "Warning",
671                                JOptionPane.WARNING_MESSAGE);
672        }
673
674        /**
675         * This methods set the {@link DefaultFileData#mimeType} to the image mime
676         * type, that should be associate with the picture.
677         */
678        void setMimeTypeByExtension(String fileExtension) {
679                String ext = fileExtension.toLowerCase();
680                if (ext.equals("jpg")) {
681                        ext = "jpeg";
682                }
683                this.mimeType = "image/" + ext;
684        }
685
686        /**
687         * If {@link #transformedPictureFile} is null, create a new temporary file,
688         * and assign it to {@link #transformedPictureFile}. Otherwise, no action.
689         *
690         * @throws IOException
691         */
692        synchronized void createTransformedTempFile() throws JUploadIOException {
693                this.uploadPolicy.displayDebug(this.hashCode()
694                                + "|Entering PictureFileData.createTransformedTempFile()", 95);
695
696                if (this.transformedPictureFile == null) {
697                        try {
698                                this.transformedPictureFile = File.createTempFile("jupload_",
699                                                ".tmp");
700                        } catch (IOException e) {
701                                throw new JUploadIOException(
702                                                "PictureFileData.createTransformedTempFile()", e);
703                        }
704                        this.uploadPolicy.getContext().registerUnload(this,
705                                        "deleteTransformedPictureFile");
706                        this.uploadPolicy.displayDebug("Using transformed temp file "
707                                        + this.transformedPictureFile.getAbsolutePath() + " for "
708                                        + getFileName(), 30);
709                }
710        }
711
712        /**
713         * This method loads the picture width and height of the picture. It's
714         * called by the current instance when necessary.
715         *
716         * @throws JUploadIOException
717         * @see #getOriginalHeight()
718         * @see #getOriginalWidth()
719         */
720        void initWidthAndHeight() throws JUploadIOException {
721                // Is it a picture?
722                if (this.isPicture
723                                && (this.originalHeight < 0 || this.originalWidth < 0)) {
724                        // Ok: it's a picture and is original width and height have not been
725                        // loaded yet.
726                        // In the windows world, file extension may be in upper case, which
727                        // is not compatible with the core Java API.
728                        Iterator<ImageReader> iter = ImageIO
729                                        .getImageReadersByFormatName(getFileExtension()
730                                                        .toLowerCase());
731                        if (iter.hasNext()) {
732                                // It's a picture: we store its original width and height, for
733                                // further calculation (rescaling and rotation).
734                                try {
735                                        FileImageInputStream fiis = new FileImageInputStream(
736                                                        getFile());
737                                        ImageReader ir = iter.next();
738                                        ir.setInput(fiis);
739                                        this.originalHeight = ir.getHeight(0);
740                                        this.originalWidth = ir.getWidth(0);
741                                        ir.dispose();
742                                        fiis.close();
743                                } catch (IOException e) {
744                                        throw new JUploadIOException("PictureFileData()", e);
745                                }
746                        }
747                }
748        }
749
750        /**
751         * If {@link #workingCopyTempFile} is null, create a new temporary file, and
752         * assign it to {@link #transformedPictureFile}. Otherwise, no action.
753         *
754         * @throws IOException
755         */
756        synchronized void createWorkingCopyTempFile() throws IOException {
757                this.uploadPolicy.displayDebug(this.hashCode()
758                                + "|Entering PictureFileData.createWorkingCopyTempFile()", 95);
759                if (this.workingCopyTempFile == null) {
760                        // The temporary file must have the correct extension, so that
761                        // native Java method works on it.
762                        this.workingCopyTempFile = File.createTempFile("jupload_", ".tmp."
763                                        + DefaultFileData.getExtension(getFile()));
764                        this.uploadPolicy.getContext().registerUnload(this,
765                                        "deleteWorkingCopyPictureFile");
766                        this.uploadPolicy.displayDebug("Using working copy temp file "
767                                        + this.workingCopyTempFile.getAbsolutePath() + " for "
768                                        + getFileName(), 30);
769                }
770        }
771
772        /**
773         * File.deleteOnExit() is pretty unreliable, especially in applets.
774         * Therefore the applet provides a callback which is executed during applet
775         * termination. This method performs the actual cleanup.
776         */
777        public synchronized void deleteWorkingCopyPictureFile() {
778                // for debug : if the debugLevel is enough, we keep the temporary
779                // file (for check).
780                if (null != this.workingCopyTempFile
781                                && this.uploadPolicy.getDebugLevel() <= 100) {
782                        if (!this.workingCopyTempFile.delete()) {
783                                this.uploadPolicy.displayWarn("Unable to delete "
784                                                + this.workingCopyTempFile.getName());
785                        }
786                        this.workingCopyTempFile = null;
787
788                }
789        }
790
791        /**
792         * Get the file that contains the original picture. This is used as a
793         * workaround for the following JVM bug: once in the navigator, it can't
794         * transform picture read from a file whose name contains non-ASCII
795         * characters, like French accents.
796         *
797         * @return The file that contains the original picture, as the source for
798         *         picture transformation
799         * @throws JUploadIOException
800         */
801        public synchronized File getWorkingSourceFile() throws JUploadIOException {
802
803                if (this.workingCopyTempFile == null) {
804                        this.uploadPolicy.displayDebug(
805                                        "[getWorkingSourceFile] Creating a copy of "
806                                                        + getFileName() + " as a source working target.",
807                                        30);
808                        FileInputStream is = null;
809                        FileOutputStream os = null;
810                        try {
811                                createWorkingCopyTempFile();
812
813                                is = new FileInputStream(getFile());
814                                os = new FileOutputStream(this.workingCopyTempFile);
815                                byte b[] = new byte[1024];
816                                int l;
817                                while ((l = is.read(b)) > 0) {
818                                        os.write(b, 0, l);
819                                }
820                        } catch (IOException e) {
821                                throw new JUploadIOException(
822                                                "ImageReaderWriterHelper.getWorkingSourceFile()", e);
823                        } finally {
824                                if (is != null) {
825                                        try {
826                                                is.close();
827                                        } catch (IOException e) {
828                                                this.uploadPolicy
829                                                                .displayWarn(e.getClass().getName()
830                                                                                + " while trying to close FileInputStream, in PictureUploadPolicy.copyOriginalToWorkingCopyTempFile.");
831                                        } finally {
832                                                is = null;
833                                        }
834                                }
835                                if (os != null) {
836                                        try {
837                                                os.close();
838                                        } catch (IOException e) {
839                                                this.uploadPolicy
840                                                                .displayWarn(e.getClass().getName()
841                                                                                + " while trying to close FileOutputStream, in PictureUploadPolicy.copyOriginalToWorkingCopyTempFile.");
842                                        } finally {
843                                                os = null;
844                                        }
845                                }
846                        }
847                }
848                return this.workingCopyTempFile;
849        }// getWorkingSourceFile()
850
851        /**
852         * @return the originalWidth of the picture
853         * @throws JUploadIOException
854         */
855        public int getOriginalWidth() throws JUploadIOException {
856                initWidthAndHeight();
857                return this.originalWidth;
858        }
859
860        /**
861         * @return the originalHeight of the picture
862         * @throws JUploadIOException
863         */
864        public int getOriginalHeight() throws JUploadIOException {
865                initWidthAndHeight();
866                return this.originalHeight;
867        }
868
869        // ////////////////////////////////////////////////////////////////////////////////////////////////////
870        // /////////////////////// static methods
871        // ////////////////////////////////////////////////////////////////////////////////////////////////////
872
873        /**
874         * Returns an ImageIcon for the given file, resized according to the given
875         * dimensions. If the original file contains a pictures smaller than these
876         * width and height, the picture is returned as is (nor resized).
877         *
878         * @param pictureFile
879         *            The file, containing a picture, from which the user wants to
880         *            extract a static picture.
881         * @param maxWidth
882         *            The maximum allowed width for the static picture to generate.
883         * @param maxHeight
884         *            The maximum allowed height for the static picture to generate.
885         * @return The created static picture, or null if the file is null.
886         * @throws JUploadException
887         *             If the ImageIcon can not be loaded.
888         */
889        public static ImageIcon getImageIcon(File pictureFile, int maxWidth,
890                        int maxHeight) throws JUploadException {
891                ImageIcon thumbnail = null;
892
893                if (pictureFile != null) {
894                        ImageIcon tmpIcon;
895                        try {
896                                tmpIcon = new ImageIcon(ImageIO.read(pictureFile));
897                        } catch (IOException e) {
898                                throw new JUploadIOException(
899                                                "An error occured while loading the image icon for "
900                                                                + pictureFile.getAbsolutePath(), e);
901                        }
902                        if (tmpIcon != null) {
903                                // Let's calculate the asked icon.
904                                double scaleWidth = ((double) maxWidth)
905                                                / tmpIcon.getIconWidth();
906                                double scaleHeight = ((double) maxHeight)
907                                                / tmpIcon.getIconHeight();
908                                double scale = Math.min(scaleWidth, scaleHeight);
909
910                                if (scale < 1) {
911                                        thumbnail = new ImageIcon(tmpIcon.getImage()
912                                                        .getScaledInstance(
913                                                                        (int) (scale * tmpIcon.getIconWidth()),
914                                                                        (int) (scale * tmpIcon.getIconHeight()),
915                                                                        Image.SCALE_FAST));
916                                } else { // no need to miniaturize
917                                        thumbnail = tmpIcon;
918                                }
919                        }
920                }
921                return thumbnail;
922        }
923
924        /**
925         * Indicates whether a file is a picture or not. The information is based on
926         * the fact the an ImageRead is found, or not, for this file. This test uses
927         * the core Java API. As in the windows world, file extension may be in
928         * uppercase, the test is based on the lowercase value for the given file
929         * extension.
930         *
931         * @param file
932         * @return true if the file can be opened as a picture, false otherwise.
933         */
934        public static boolean isFileAPicture(File file) {
935                // In the windows world, file extension may be in uppercase, which is
936                // not compatible with the core Java API.
937                Iterator<ImageReader> iter = ImageIO
938                                .getImageReadersByFormatName(DefaultFileData.getExtension(file)
939                                                .toLowerCase());
940                return iter.hasNext();
941        }
942}
Note: See TracBrowser for help on using the repository browser.