source: 3thparty/jupload/src/main/java/wjhk/jupload2/gui/JUploadFileView.java @ 3951

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

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

Line 
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: 2009-07-02 11:49:12 -0300 (Qui, 02 Jul 2009) $
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.gui;
23
24import java.awt.image.BufferedImage;
25import java.beans.PropertyChangeEvent;
26import java.beans.PropertyChangeListener;
27import java.io.File;
28import java.util.Enumeration;
29import java.util.concurrent.ConcurrentHashMap;
30import java.util.concurrent.ExecutorService;
31import java.util.concurrent.Executors;
32import java.util.concurrent.ThreadFactory;
33
34import javax.swing.Icon;
35import javax.swing.ImageIcon;
36import javax.swing.JFileChooser;
37import javax.swing.filechooser.FileView;
38
39import wjhk.jupload2.filedata.PictureFileData;
40import wjhk.jupload2.policies.DefaultUploadPolicy;
41import wjhk.jupload2.policies.PictureUploadPolicy;
42import 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 */
63class 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    UploadPolicy uploadPolicy = null;
93
94    /** The current file chooser. */
95    JFileChooser fileChooser = null;
96
97    /** The current file view */
98    JUploadFileView fileView = null;
99
100    /** The file whose icon must be loaded. */
101    File file = null;
102
103    /** The icon for this file. */
104    Icon icon = null;
105
106    /** Current loading status for this worker */
107    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            JUploadFileView fileView, File file) {
117        this.uploadPolicy = uploadPolicy;
118        this.fileChooser = fileChooser;
119        this.fileView = fileView;
120        this.file = file;
121    }
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        switch (this.status) {
130            case STATUS_LOADED:
131                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                this.fileView.execute(this);
137                return JUploadFileView.emptyIcon;
138            default:
139                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            if (this.status == STATUS_TO_BE_LOADED) {
151                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                Thread.yield();
156
157                // This class is used only to do this call, in a separate
158                // thread.
159                this.icon = this.uploadPolicy.fileViewGetIcon(this.file);
160                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                Thread.yield();
165
166                // Let's notify the fact the work is done.
167                this.fileChooser.repaint();
168
169                // A try to minimize memory footprint
170                PictureFileData.freeMemory(this.getClass().getName()
171                        + ".loadIcon()", this.uploadPolicy);
172            }
173        } catch (OutOfMemoryError e) {
174            this.uploadPolicy
175                    .displayWarn("OutOfMemoryError in IconWorker.loadIcon()");
176            this.status = STATUS_ERROR_WHILE_LOADING;
177            this.icon = null;
178        }
179    }
180
181    /** Implementation of the Runnable interface */
182    public void run() {
183        loadIcon();
184    }
185}
186
187// //////////////////////////////////////////////////////////////////////////////////////////////////
188// ///////////////// JUploadFileView
189// //////////////////////////////////////////////////////////////////////////////////////////////////
190
191/**
192 * This class provides the icon view for the file selector.
193 *
194 * @author etienne_sf
195 */
196public class JUploadFileView extends FileView implements
197        PropertyChangeListener, ThreadFactory {
198
199    /**
200     * This thread group is used to contain all icon worker threads. Its
201     * priority is the MIN_PRIORITY, to try to minimize CPU footprint. Its
202     * thread max priority is set in the
203     * {@link JUploadFileView#JUploadFileView(UploadPolicy, JFileChooser)}
204     * constructor.
205     */
206    ThreadGroup iconWorkerThreadGroup = new ThreadGroup("JUpload ThreadGroup");
207
208    /** The current upload policy. */
209    UploadPolicy uploadPolicy = null;
210
211    /** The current file chooser. */
212    JFileChooser fileChooser = null;
213
214    /** This map will contain all instances of {@link IconWorker}. */
215    ConcurrentHashMap<String, IconWorker> hashMap = new ConcurrentHashMap<String, IconWorker>(
216            1000, (float) 0.5, 3);
217
218    /**
219     * This executor will crate icons from files, one at a time. It is used to
220     * create these icon asynchronously.
221     *
222     * @see #execute(IconWorker)
223     */
224    ExecutorService executorService = null;
225
226    /**
227     * An empty icon, having the good file size.
228     */
229    public static ImageIcon emptyIcon = null;
230
231    /**
232     * Creates a new instance.
233     *
234     * @param uploadPolicy The upload policy to apply.
235     * @param fileChooser The desired file chooser to use.
236     */
237    public JUploadFileView(UploadPolicy uploadPolicy, JFileChooser fileChooser) {
238        this.uploadPolicy = uploadPolicy;
239        this.fileChooser = fileChooser;
240        this.fileChooser.addPropertyChangeListener(this);
241
242        // The real interest of the thread group, here, is to lower the priority
243        // of the icon workers threads:
244        this.iconWorkerThreadGroup.setMaxPriority(Thread.MIN_PRIORITY);
245
246        // emptyIcon needs an upload policy, to be set, but we'll create it
247        // only once.
248        if (emptyIcon == null
249                || emptyIcon.getIconHeight() != uploadPolicy
250                        .getFileChooserIconSize()) {
251            // The empty icon has not been calculated yet, or its size changed
252            // since the icon creation. This can happen when the applet is
253            // reloaded, and the applet parameter changed: the static attribute
254            // are not recalculated.
255            // Let's construct the resized picture.
256            emptyIcon = new ImageIcon(new BufferedImage(uploadPolicy
257                    .getFileChooserIconSize(), uploadPolicy
258                    .getFileChooserIconSize(), BufferedImage.TYPE_INT_ARGB_PRE));
259        }
260    }
261
262    synchronized void execute(IconWorker iconWorker) {
263        if (this.executorService == null || this.executorService.isShutdown()) {
264            this.executorService = Executors.newSingleThreadExecutor();
265        }
266        iconWorker.status = IconWorker.STATUS_TO_BE_LOADED;
267        this.executorService.execute(iconWorker);
268    }
269
270    /**
271     * Stop all current and to come thread. To be called when the file chooser
272     * is closed.
273     */
274    synchronized public void shutdownNow() {
275        if (this.executorService != null) {
276            stopRunningJobs();
277
278            this.executorService.shutdownNow();
279            this.executorService = null;
280        }
281    }
282
283    /**
284     * Lazily mark all jobs as not done. No particular thread management.
285     */
286    private void stopRunningJobs() {
287        Enumeration<IconWorker> e = this.hashMap.elements();
288        IconWorker iw = null;
289        while (e.hasMoreElements()) {
290            iw = e.nextElement();
291            if (iw.status == IconWorker.STATUS_TO_BE_LOADED) {
292                iw.status = IconWorker.STATUS_NOT_LOADED;
293            }
294        }
295    }
296
297    /**
298     * Waiting for JFileChooser events. Currently managed:
299     * DIRECTORY_CHANGED_PROPERTY, to stop the to be loaded icons.
300     *
301     * @param e
302     */
303    public void propertyChange(PropertyChangeEvent e) {
304        String prop = e.getPropertyName();
305        // If the directory changed, don't show an image.
306        if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)) {
307            // We stops all running job. If the user gets back to this
308            // directory, the non calculated icons will be added to the job
309            // list.
310            this.uploadPolicy.displayDebug(
311                    "[JUploadFileView] Directory changed", 50);
312            stopRunningJobs();
313        }
314    }
315
316    // ///////////////////////////////////////////////////////////////////////:
317    // /////////////////////// Methods from the FileView class
318    // ///////////////////////////////////////////////////////////////////////:
319
320    /** #see javax.swing.filechooser.FileView#getDescription(File)) */
321    @Override
322    public String getDescription(File f) {
323        return null; // let the L&F FileView figure this out
324    }
325
326    /**
327     * The fileChooserIconFromFileContent applet parameter defies which icon is
328     * to be returned here.
329     *
330     * @see javax.swing.filechooser.FileView#getIcon(java.io.File)
331     * @see UploadPolicy#PROP_FILE_CHOOSER_ICON_FROM_FILE_CONTENT
332     */
333    @Override
334    public Icon getIcon(File file) {
335        // For DefaultUploadPolicy, a value of 1 means calculating the icon.
336        // For PictureUploadPolicy and sisters, a value of 0 also means
337        // calculating the icon
338        // Otherwise: return null, for default icon.
339        if (!((this.uploadPolicy.getFileChooserIconFromFileContent() == 1 && this.uploadPolicy instanceof DefaultUploadPolicy) || (this.uploadPolicy
340                .getFileChooserIconFromFileContent() == 0 && this.uploadPolicy instanceof PictureUploadPolicy))) {
341            return null;
342        }
343        // For PictureUploadPolicy and sisters, a value of 0
344        if (file.isDirectory()) {
345            // We let the L&F display the system icon for directories.
346            return null;
347        }
348        IconWorker iconWorker = this.hashMap.get(file.getAbsolutePath());
349        if (iconWorker == null) {
350            // This file has not been loaded.
351            iconWorker = new IconWorker(this.uploadPolicy, this.fileChooser,
352                    this, file);
353            // We store it in the global Icon container.
354            this.hashMap.put(file.getAbsolutePath(), iconWorker);
355            // Then, we ask the current Thread to load its icon. It will be done
356            // later.
357            execute(iconWorker);
358            // We currently have no icon to display.
359            return emptyIcon;
360        }
361        // Ok, let's take the icon.
362        return iconWorker.getIcon();
363    }
364
365    /** #see {@link javax.swing.filechooser.FileView#getName(File)} */
366    @Override
367    public String getName(File f) {
368        return null; // let the L&F FileView figure this out
369    }
370
371    /** #see {@link javax.swing.filechooser.FileView#getTypeDescription(File)} */
372    @Override
373    public String getTypeDescription(File f) {
374        return null; // let the L&F FileView figure this out
375    }
376
377    /** #see {@link javax.swing.filechooser.FileView#isTraversable(File)} */
378    @Override
379    public Boolean isTraversable(File f) {
380        return null; // let the L&F FileView figure this out
381    }
382
383    /**
384     * Implementation of ThreadFactory. Creates a thread in the
385     * iconWorkerThreadGroup thread group. This thread group has the lower
386     * available priority.
387     *
388     * @param runnable The runnable instance to start.
389     * @return The newly created thread
390     */
391    public Thread newThread(Runnable runnable) {
392        Thread thread = new Thread(this.iconWorkerThreadGroup, runnable);
393        thread.setPriority(Thread.MIN_PRIORITY);
394        return thread;
395    }
396}
Note: See TracBrowser for help on using the repository browser.