Coverage Report - wjhk.jupload2.gui.JUploadFileView
 
Classes in this File Line Coverage Branch Coverage Complexity
IconWorker
0 %
0/35
0 %
0/5
2,533
JUploadFileView
0 %
0/59
0 %
0/28
2,533
 
 1  
 //
 2  
 // $Id: JUploadFileView.java 112 2007-05-07 02:45:28 +0000 (lun., 07 mai 2007)
 3  
 // felfert $
 4  
 //
 5  
 // jupload - A file upload applet.
 6  
 // Copyright 2007 The JUpload Team
 7  
 //
 8  
 // Created: 2007-04-06
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date: 2011-04-29 15:46:52 +0200 (ven., 29 avr. 2011) $
 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  
 
 22  
 package wjhk.jupload2.gui;
 23  
 
 24  
 import java.awt.image.BufferedImage;
 25  
 import java.beans.PropertyChangeEvent;
 26  
 import java.beans.PropertyChangeListener;
 27  
 import java.io.File;
 28  
 import java.util.Enumeration;
 29  
 import java.util.concurrent.ConcurrentHashMap;
 30  
 import java.util.concurrent.ExecutorService;
 31  
 import java.util.concurrent.Executors;
 32  
 import java.util.concurrent.ThreadFactory;
 33  
 
 34  
 import javax.swing.Icon;
 35  
 import javax.swing.ImageIcon;
 36  
 import javax.swing.JFileChooser;
 37  
 import javax.swing.filechooser.FileView;
 38  
 
 39  
 import wjhk.jupload2.filedata.PictureFileData;
 40  
 import wjhk.jupload2.policies.DefaultUploadPolicy;
 41  
 import wjhk.jupload2.policies.PictureUploadPolicy;
 42  
 import wjhk.jupload2.policies.UploadPolicy;
 43  
 
 44  
 // //////////////////////////////////////////////////////////////////////////////////////////////////
 45  
 // ///////////////////////////// local class: JUploadFileView
 46  
 // //////////////////////////////////////////////////////////////////////////////////////////////////
 47  
 
 48  
 /**
 49  
  * The IconWorker class loads a icon from a file. It's called from a backup
 50  
  * thread created by the JUploadFileView class. This allows to load/calculate
 51  
  * icons in background. This prevent the applet to be freezed while icons are
 52  
  * loading. <BR>
 53  
  * Instances of this class can have the following status, in this order: <DIR>
 54  
  * <LI>STATUS_NOT_LOADED: This icon is not loaded, and its loading is not
 55  
  * requested. This status is the default one, on creation. <LI>
 56  
  * STATUS_TO_BE_LOADED: This icon is on the list of icon to load. This status is
 57  
  * written by the JUploadFileView#execute(IconWorker) method. <LI>
 58  
  * STATUS_LOADING: Indicates the IconWorker#loadIcon() has been called, but is
 59  
  * not finished. <LI>STATUS_LOADED: The icon is loaded, and ready to be
 60  
  * displayed. <LI>STATUS_ERROR_WHILE_LOADING: Too bad, the applet could not load
 61  
  * the icon. It won't be tried again. </DIR>
 62  
  */
 63  
 class IconWorker implements Runnable {
 64  
 
 65  
     /** Indicates that an error occurs, during the icon creation */
 66  
     final static int STATUS_ERROR_WHILE_LOADING = -1;
 67  
 
 68  
     /** Indicates that the icon for this file has been loaded */
 69  
     final static int STATUS_LOADED = 1;
 70  
 
 71  
     /**
 72  
      * Indicated that the creation of the icon for this file has started. But it
 73  
      * is not ready yet.
 74  
      */
 75  
     final static int STATUS_LOADING = 2;
 76  
 
 77  
     /**
 78  
      * Indicates the loading of the icon for this file has been requested, but
 79  
      * has not started yet.
 80  
      */
 81  
     final static int STATUS_TO_BE_LOADED = 3;
 82  
 
 83  
     /**
 84  
      * Indicates the loading of the icon for this file is not currently
 85  
      * requested. The loading may have been requested, then cancelled, for
 86  
      * instance of the user changes the current directory or closes the file
 87  
      * chooser.
 88  
      */
 89  
     final static int STATUS_NOT_LOADED = 4;
 90  
 
 91  
     /** The current upload policy */
 92  0
     UploadPolicy uploadPolicy = null;
 93  
 
 94  
     /** The current file chooser. */
 95  0
     JFileChooser fileChooser = null;
 96  
 
 97  
     /** The current file view */
 98  0
     JUploadFileView fileView = null;
 99  
 
 100  
     /** The file whose icon must be loaded. */
 101  0
     File file = null;
 102  
 
 103  
     /** The icon for this file. */
 104  0
     Icon icon = null;
 105  
 
 106  
     /** Current loading status for this worker */
 107  0
     int status = STATUS_NOT_LOADED;
 108  
 
 109  
     /**
 110  
      * The constructor only stores the file. The background thread will call the
 111  
      * loadIcon method.
 112  
      * 
 113  
      * @param file The file whose icon must be loaded/calculated.
 114  
      */
 115  
     IconWorker(UploadPolicy uploadPolicy, JFileChooser fileChooser,
 116  0
             JUploadFileView fileView, File file) {
 117  0
         this.uploadPolicy = uploadPolicy;
 118  0
         this.fileChooser = fileChooser;
 119  0
         this.fileView = fileView;
 120  0
         this.file = file;
 121  0
     }
 122  
 
 123  
     /**
 124  
      * Returns the currently loaded icon for this file.
 125  
      * 
 126  
      * @return The Icon to be displayed for this file.
 127  
      */
 128  
     Icon getIcon() {
 129  0
         switch (this.status) {
 130  
             case STATUS_LOADED:
 131  0
                 return this.icon;
 132  
             case STATUS_NOT_LOADED:
 133  
                 // ?? This picture should not be in this state. Perhaps the user
 134  
                 // changes of directory, then went back to it.
 135  
                 // We ask again to calculate its icon.
 136  0
                 this.fileView.execute(this);
 137  0
                 return JUploadFileView.emptyIcon;
 138  
             default:
 139  0
                 return JUploadFileView.emptyIcon;
 140  
         }// switch
 141  
     }// getIcon
 142  
 
 143  
     /**
 144  
      * Get the icon from the current upload policy, for this file. This methods
 145  
      * does something only if the current status for the icon is
 146  
      * {@link #STATUS_TO_BE_LOADED}. If not, this method does nothing.
 147  
      */
 148  
     void loadIcon() {
 149  
         try {
 150  0
             if (this.status == STATUS_TO_BE_LOADED) {
 151  0
                 this.status = STATUS_LOADING;
 152  
 
 153  
                 // This thread is of the lower possible priority. So we first
 154  
                 // give a change for other thread to work
 155  0
                 Thread.yield();
 156  
 
 157  
                 // This class is used only to do this call, in a separate
 158  
                 // thread.
 159  0
                 this.icon = this.uploadPolicy.fileViewGetIcon(this.file);
 160  0
                 this.status = STATUS_LOADED;
 161  
 
 162  
                 // This thread is of the lower possible priority. So we first
 163  
                 // give a change for other thread to work
 164  0
                 Thread.yield();
 165  
 
 166  
                 // Let's notify the fact the work is done.
 167  0
                 this.fileChooser.repaint();
 168  
 
 169  
                 // A try to minimize memory footprint
 170  0
                 PictureFileData.freeMemory(this.getClass().getName()
 171  
                         + ".loadIcon()", this.uploadPolicy);
 172  
             }
 173  0
         } catch (OutOfMemoryError e) {
 174  0
             this.uploadPolicy
 175  0
                     .displayWarn("OutOfMemoryError in IconWorker.loadIcon() ["
 176  0
                             + e.getMessage() + "]");
 177  0
             this.status = STATUS_ERROR_WHILE_LOADING;
 178  0
             this.icon = null;
 179  0
         }
 180  0
     }
 181  
 
 182  
     /** Implementation of the Runnable interface */
 183  
     public void run() {
 184  0
         loadIcon();
 185  0
     }
 186  
 }
 187  
 
 188  
 // //////////////////////////////////////////////////////////////////////////////////////////////////
 189  
 // ///////////////// JUploadFileView
 190  
 // //////////////////////////////////////////////////////////////////////////////////////////////////
 191  
 
 192  
 /**
 193  
  * This class provides the icon view for the file selector.
 194  
  * 
 195  
  * @author etienne_sf
 196  
  */
 197  
 public class JUploadFileView extends FileView implements
 198  
         PropertyChangeListener, ThreadFactory {
 199  
 
 200  
     /**
 201  
      * This thread group is used to contain all icon worker threads. Its
 202  
      * priority is the MIN_PRIORITY, to try to minimize CPU footprint. Its
 203  
      * thread max priority is set in the
 204  
      * {@link JUploadFileView#JUploadFileView(UploadPolicy, JFileChooser)}
 205  
      * constructor.
 206  
      */
 207  0
     ThreadGroup iconWorkerThreadGroup = new ThreadGroup("JUpload ThreadGroup");
 208  
 
 209  
     /** The current upload policy. */
 210  0
     UploadPolicy uploadPolicy = null;
 211  
 
 212  
     /** The current file chooser. */
 213  0
     JFileChooser fileChooser = null;
 214  
 
 215  
     /** This map will contain all instances of {@link IconWorker}. */
 216  0
     ConcurrentHashMap<String, IconWorker> hashMap = new ConcurrentHashMap<String, IconWorker>(
 217  
             1000, (float) 0.5, 3);
 218  
 
 219  
     /**
 220  
      * This executor will crate icons from files, one at a time. It is used to
 221  
      * create these icon asynchronously.
 222  
      * 
 223  
      * @see #execute(IconWorker)
 224  
      */
 225  0
     ExecutorService executorService = null;
 226  
 
 227  
     /**
 228  
      * An empty icon, having the good file size.
 229  
      */
 230  0
     public static ImageIcon emptyIcon = null;
 231  
 
 232  
     /**
 233  
      * Creates a new instance.
 234  
      * 
 235  
      * @param uploadPolicy The upload policy to apply.
 236  
      * @param fileChooser The desired file chooser to use.
 237  
      */
 238  0
     public JUploadFileView(UploadPolicy uploadPolicy, JFileChooser fileChooser) {
 239  0
         this.uploadPolicy = uploadPolicy;
 240  0
         this.fileChooser = fileChooser;
 241  0
         this.fileChooser.addPropertyChangeListener(this);
 242  
 
 243  
         // The real interest of the thread group, here, is to lower the priority
 244  
         // of the icon workers threads:
 245  0
         this.iconWorkerThreadGroup.setMaxPriority(Thread.MIN_PRIORITY);
 246  
 
 247  
         // emptyIcon needs an upload policy, to be set, but we'll create it
 248  
         // only once.
 249  0
         if (emptyIcon == null
 250  0
                 || emptyIcon.getIconHeight() != uploadPolicy
 251  0
                         .getFileChooserIconSize()) {
 252  
             // The empty icon has not been calculated yet, or its size changed
 253  
             // since the icon creation. This can happen when the applet is
 254  
             // reloaded, and the applet parameter changed: the static attribute
 255  
             // are not recalculated.
 256  
             // Let's construct the resized picture.
 257  0
             emptyIcon = new ImageIcon(new BufferedImage(uploadPolicy
 258  0
                     .getFileChooserIconSize(), uploadPolicy
 259  0
                     .getFileChooserIconSize(), BufferedImage.TYPE_INT_ARGB_PRE));
 260  
         }
 261  0
     }
 262  
 
 263  
     synchronized void execute(IconWorker iconWorker) {
 264  0
         if (this.executorService == null || this.executorService.isShutdown()) {
 265  0
             this.executorService = Executors.newSingleThreadExecutor();
 266  
         }
 267  0
         iconWorker.status = IconWorker.STATUS_TO_BE_LOADED;
 268  0
         this.executorService.execute(iconWorker);
 269  0
     }
 270  
 
 271  
     /**
 272  
      * Stop all current and to come thread. To be called when the file chooser
 273  
      * is closed.
 274  
      */
 275  
     synchronized public void shutdownNow() {
 276  0
         if (this.executorService != null) {
 277  0
             stopRunningJobs();
 278  
 
 279  0
             this.executorService.shutdownNow();
 280  0
             this.executorService = null;
 281  
         }
 282  0
     }
 283  
 
 284  
     /**
 285  
      * Lazily mark all jobs as not done. No particular thread management.
 286  
      */
 287  
     private void stopRunningJobs() {
 288  0
         Enumeration<IconWorker> e = this.hashMap.elements();
 289  0
         IconWorker iw = null;
 290  0
         while (e.hasMoreElements()) {
 291  0
             iw = e.nextElement();
 292  0
             if (iw.status == IconWorker.STATUS_TO_BE_LOADED) {
 293  0
                 iw.status = IconWorker.STATUS_NOT_LOADED;
 294  
             }
 295  
         }
 296  0
     }
 297  
 
 298  
     /**
 299  
      * Waiting for JFileChooser events. Currently managed:
 300  
      * DIRECTORY_CHANGED_PROPERTY, to stop the to be loaded icons.
 301  
      * 
 302  
      * @param e
 303  
      */
 304  
     public void propertyChange(PropertyChangeEvent e) {
 305  0
         String prop = e.getPropertyName();
 306  
         // If the directory changed, don't show an image.
 307  0
         if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)) {
 308  
             // We stops all running job. If the user gets back to this
 309  
             // directory, the non calculated icons will be added to the job
 310  
             // list.
 311  0
             this.uploadPolicy.displayDebug(
 312  
                     "[JUploadFileView] Directory changed", 50);
 313  0
             stopRunningJobs();
 314  
         }
 315  0
     }
 316  
 
 317  
     // ///////////////////////////////////////////////////////////////////////:
 318  
     // /////////////////////// Methods from the FileView class
 319  
     // ///////////////////////////////////////////////////////////////////////:
 320  
 
 321  
     /** #see javax.swing.filechooser.FileView#getDescription(File)) */
 322  
     @Override
 323  
     public String getDescription(File f) {
 324  0
         return null; // let the L&F FileView figure this out
 325  
     }
 326  
 
 327  
     /**
 328  
      * The fileChooserIconFromFileContent applet parameter defies which icon is
 329  
      * to be returned here.
 330  
      * 
 331  
      * @see javax.swing.filechooser.FileView#getIcon(java.io.File)
 332  
      * @see UploadPolicy#PROP_FILE_CHOOSER_ICON_FROM_FILE_CONTENT
 333  
      */
 334  
     @Override
 335  
     public Icon getIcon(File file) {
 336  
         // For DefaultUploadPolicy, a value of 1 means calculating the icon.
 337  
         // For PictureUploadPolicy and sisters, a value of 0 also means
 338  
         // calculating the icon
 339  
         // Otherwise: return null, for default icon.
 340  0
         if (!((this.uploadPolicy.getFileChooserIconFromFileContent() == 1 && this.uploadPolicy instanceof DefaultUploadPolicy) || (this.uploadPolicy
 341  0
                 .getFileChooserIconFromFileContent() == 0 && this.uploadPolicy instanceof PictureUploadPolicy))) {
 342  0
             return null;
 343  
         }
 344  
         // For PictureUploadPolicy and sisters, a value of 0
 345  0
         if (file.isDirectory()) {
 346  
             // We let the L&F display the system icon for directories.
 347  0
             return null;
 348  
         }
 349  0
         IconWorker iconWorker = this.hashMap.get(file.getAbsolutePath());
 350  0
         if (iconWorker == null) {
 351  
             // This file has not been loaded.
 352  0
             iconWorker = new IconWorker(this.uploadPolicy, this.fileChooser,
 353  
                     this, file);
 354  
             // We store it in the global Icon container.
 355  0
             this.hashMap.put(file.getAbsolutePath(), iconWorker);
 356  
             // Then, we ask the current Thread to load its icon. It will be done
 357  
             // later.
 358  0
             execute(iconWorker);
 359  
             // We currently have no icon to display.
 360  0
             return emptyIcon;
 361  
         }
 362  
         // Ok, let's take the icon.
 363  0
         return iconWorker.getIcon();
 364  
     }
 365  
 
 366  
     /** #see {@link javax.swing.filechooser.FileView#getName(File)} */
 367  
     @Override
 368  
     public String getName(File f) {
 369  0
         return null; // let the L&F FileView figure this out
 370  
     }
 371  
 
 372  
     /** #see {@link javax.swing.filechooser.FileView#getTypeDescription(File)} */
 373  
     @Override
 374  
     public String getTypeDescription(File f) {
 375  0
         return null; // let the L&F FileView figure this out
 376  
     }
 377  
 
 378  
     /** #see {@link javax.swing.filechooser.FileView#isTraversable(File)} */
 379  
     @Override
 380  
     public Boolean isTraversable(File f) {
 381  0
         return null; // let the L&F FileView figure this out
 382  
     }
 383  
 
 384  
     /**
 385  
      * Implementation of ThreadFactory. Creates a thread in the
 386  
      * iconWorkerThreadGroup thread group. This thread group has the lower
 387  
      * available priority.
 388  
      * 
 389  
      * @param runnable The runnable instance to start.
 390  
      * @return The newly created thread
 391  
      */
 392  
     public Thread newThread(Runnable runnable) {
 393  0
         Thread thread = new Thread(this.iconWorkerThreadGroup, runnable);
 394  0
         thread.setPriority(Thread.MIN_PRIORITY);
 395  0
         return thread;
 396  
     }
 397  
 }