View Javadoc
1   //
2   // $Id: FilePanelFlatDataModel2.java 1721 2015-03-29 17:48:44Z etienne_sf $
3   //
4   // jupload - A file upload applet.
5   // Copyright 2007 The JUpload Team
6   //
7   // Created: 2006-04-21
8   // Creator: etienne_sf
9   // Last modified: $Date: 2015-03-29 19:48:44 +0200 (dim., 29 mars 2015) $
10  //
11  // This program is free software; you can redistribute it and/or modify it under
12  // the terms of the GNU General Public License as published by the Free Software
13  // Foundation; either version 2 of the License, or (at your option) any later
14  // version. This program is distributed in the hope that it will be useful, but
15  // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  // details. You should have received a copy of the GNU General Public License
18  // along with this program; if not, write to the Free Software Foundation, Inc.,
19  // 675 Mass Ave, Cambridge, MA 02139, USA.
20  
21  package wjhk.jupload2.gui.filepanel;
22  
23  import java.io.File;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Date;
27  import java.util.List;
28  
29  import javax.swing.table.AbstractTableModel;
30  
31  import wjhk.jupload2.exception.JUploadExceptionStopAddingFiles;
32  import wjhk.jupload2.filedata.DefaultFileData;
33  import wjhk.jupload2.filedata.FileData;
34  import wjhk.jupload2.policies.UploadPolicy;
35  
36  /**
37   * This class replaces FilePanelDataModel. The data for each row is now contained in an instance of FileData (or one of
38   * its subclasses, like {@link wjhk.jupload2.filedata.PictureFileData}). This allow easy add of new functionalites,
39   * during upload, by adding attributes or methods to these classes, or create new ones. <BR>
40   * Some ides of improvements :
41   * <UL>
42   * <LI>Compression of picture before Upload (see {@link wjhk.jupload2.filedata.PictureFileData})
43   * <LI>Could be XML validation before sending to the server
44   * <LI>Up to your imagination...
45   * </UL>
46   */
47  public class FilePanelFlatDataModel2 extends AbstractTableModel {
48  
49      /** A generated serialVersionUID, to avoid warning during compilation */
50      private static final long serialVersionUID = 1473262424494858913L;
51  
52      /**
53       * The default colum indices of the columns, as displayed by the applet. Index of the column "Filename"
54       */
55      public final static int COLINDEX_NAME = 0;
56  
57      /**
58       * The default colum indices of the columns, as displayed by the applet. Index of the column "Filename"
59       */
60      public final static int COLINDEX_SIZE = 1;
61  
62      /**
63       * The default colum indices of the columns, as displayed by the applet. Index of the column "Filesize"
64       */
65      public final static int COLINDEX_DIRECTORY = 2;
66  
67      /**
68       * The default colum indices of the columns, as displayed by the applet. Index of the column "Last modified"
69       */
70      public final static int COLINDEX_MODIFIED = 3;
71  
72      /**
73       * The uploadPolicy contains all current parameter, including the FileDataParam
74       */
75      private UploadPolicy uploadPolicy = null;
76  
77      /**
78       * The column names, as displayed on the applet. They are not real final values, as they are translated: we need to
79       * have an uploadPolicy for this translation, and the uploadPolicy is 'given' to the constructor.
80       */
81      private String COL_NAME = null;
82  
83      private String COL_SIZE = null;
84  
85      private String COL_DIRECTORY = null;
86  
87      private String COL_MODIFIED = null;
88  
89      private String COL_CHECKED = "";
90  
91      protected String[] columnNames = null;
92  
93      /**
94       * This array indicates, for each column, the percentage of the available width it should use. It's initialized in
95       * the constructor of this class.
96       * 
97       * @see #getColumnSize(int)
98       */
99      protected int[] columnSizePercentage = null;
100 
101     /**
102      * Indicates whether each column is editable or not. Only the check box should be editable.
103      */
104     protected boolean[] columnEditable = null;
105 
106     protected Class<?> columnClasses[] = null;
107 
108     /**
109      * This Vector contains all FileData.
110      */
111     private List<FileData> rows = new ArrayList<FileData>();
112 
113     /**
114      * @param uploadPolicy
115      */
116     public FilePanelFlatDataModel2(UploadPolicy uploadPolicy) {
117         // Property initialization is done ... for each property. Nothing to do
118         // here.
119         super();
120         //
121         this.uploadPolicy = uploadPolicy;
122 
123         // Initialization for column name, type and size.
124         this.COL_NAME = uploadPolicy.getLocalizedString("colName");
125         this.COL_SIZE = uploadPolicy.getLocalizedString("colSize");
126         this.COL_DIRECTORY = uploadPolicy.getLocalizedString("colDirectory");
127         this.COL_MODIFIED = uploadPolicy.getLocalizedString("colModified");
128 
129         this.columnNames = new String[] {
130                 this.COL_NAME, this.COL_SIZE, this.COL_DIRECTORY, this.COL_MODIFIED, this.COL_CHECKED
131         // COL_CHECKED is unnamed.
132         };
133 
134         // Initial size (before percentage) was: 150, 75, 199, 130, 75
135         this.columnSizePercentage = new int[] {
136                 29, 11, 35, 20, 5
137         };
138 
139         // Initial size (before percentage) was: 150, 75, 199, 130, 75
140         this.columnEditable = new boolean[] {
141                 false, false, false, false, true
142         };
143 
144         // Check that the sum of the previous values is actually 100%
145         int total = 0;
146         for (int i = 0; i < this.columnSizePercentage.length; i += 1) {
147             total += this.columnSizePercentage[i];
148         }
149         if (total != 100) {
150             throw new java.lang.AssertionError("Total sum of '" + this.getClass().getName()
151                     + ".columnSizePercentage' should be 100% (but was " + total + ")");
152         }
153 
154         this.columnClasses = new Class[] {
155                 String.class, Long.class, String.class, Date.class, Boolean.class, Boolean.class
156         };
157     }
158 
159     /**
160      * Get the FileData representing a given file.
161      * 
162      * @param file : the file that could be contained...
163      * @return The FileData representing this file. Null if the file is not already contained in the file list.
164      */
165     public FileData contains(String absolutePath) {
166         FileData foundFileData = null;
167 
168         for (FileData fd : rows) {
169             if (absolutePath.equals(fd.getAbsolutePath())) {
170                 foundFileData = fd;
171                 break;
172             }
173         } // for
174 
175         return foundFileData;
176     }
177 
178     /**
179      * Add a file to the panel (at the end of the list)
180      * 
181      * @param file
182      * @param root
183      * @throws JUploadExceptionStopAddingFiles
184      */
185     public FileData addFile(File file) throws JUploadExceptionStopAddingFiles {
186         synchronized (this.rows) {
187             FileData foundFileData = contains(file.getAbsolutePath());
188             if (foundFileData != null) {
189                 this.uploadPolicy.displayWarn("File " + file.getName() + " already exists");
190                 return foundFileData;
191             } else {
192                 // We first call the upload policy, to get :
193                 // - The correct fileData instance (for instance the
194                 // PictureUploadPolicy returns a PictureFileData)
195                 // - The reference to this newly FileData, or null if an error
196                 // occurs (for instance: invalid file content, according to the
197                 // current upload policy, or non allowed file extension).
198                 FileData fd = this.uploadPolicy.createFileData(file);
199                 if (fd != null) {
200                     // The file is Ok, let's add it.
201                     this.rows.add(fd);
202 
203                     // It's slow to call fireTableDataChanged(); for each file. So, this call is done when all
204                     // files have been added. This is done in FilePanelTableImp.addFiles
205                 }
206                 return fd;
207             }// else
208         }// synchronized
209     }
210 
211     /**
212      * Ask for the file contained at specified row number.
213      * 
214      * @param row The row number
215      * @return The return instance of File.
216      */
217     public FileData getFileDataAt(int row) {
218         if (row >= 0) {
219             try {
220                 return this.rows.get(row);
221             } catch (ArrayIndexOutOfBoundsException e) {
222                 // Nothing to do. It seems that it can occurs when upload is very fast (for instance: small files to
223                 // localhost).
224                 this.uploadPolicy.displayWarn(e.getClass().getName() + " in FilePanelDataModel2.getFileDataAt(" + row
225                         + ")");
226             }
227         }
228         return null;
229     }
230 
231     /**
232      * Remove a specified row.
233      * 
234      * @param row The row to remove.
235      */
236     public void removeRow(int row) {
237         this.rows.remove(row);
238         fireTableDataChanged();
239     }
240 
241     /**
242      * Returns the row number, for the given fileData, in the current list.
243      * 
244      * @param fileData The {@link FileData} to find
245      * @return The row number for fileData, or -1 if it was not found.
246      */
247     public int getRow(FileData fileData) {
248         synchronized (this.rows) {
249             for (int i = 0; i < this.rows.size(); i += 1) {
250                 if (rows.get(i).getAbsolutePath().equals(fileData.getAbsolutePath())) {
251                     return i;
252                 }
253             }// for
254             return -1;
255         }// synchronized(rows)
256     }
257 
258     /** @see javax.swing.table.TableModel#getColumnCount() */
259     public int getColumnCount() {
260         return this.columnNames.length;
261     }
262 
263     /** @see javax.swing.table.TableModel#getRowCount() */
264     public int getRowCount() {
265         return this.rows.size();
266     }
267 
268     /**
269      * Always return false here : no editable cell.
270      * 
271      * @see javax.swing.table.TableModel#isCellEditable(int, int)
272      */
273     @Override
274     public boolean isCellEditable(int arg0, int arg1) {
275         return columnEditable[arg1];
276     }
277 
278     /**
279      * Sort the rows, according to one column.
280      * 
281      * @param col The index of the column to sort
282      * @param ascending true if ascending, false if descending.
283      */
284     public void sortColumn(int col, boolean ascending) {
285         synchronized (this.rows) {
286             Collections.sort(this.rows, new ColumnComparator(col, ascending));
287         }
288         fireTableDataChanged();
289     }
290 
291     /**
292      * Return true if this column can be sorted.
293      * 
294      * @param col The index of the column which can sortable or not.
295      * @return true if the column can be sorted. false otherwise.
296      */
297     public boolean isSortable(int col) {
298         return (Boolean.class != getColumnClass(col));
299     }
300 
301     /**
302      * @see javax.swing.table.TableModel#getColumnClass(int)
303      */
304     @Override
305     public Class<?> getColumnClass(int arg0) {
306         return this.columnClasses[arg0];
307     }
308 
309     /**
310      * @see javax.swing.table.TableModel#getValueAt(int, int)
311      */
312     public Object getValueAt(int row, int col) {
313         FileData fileData = getFileDataAt(row);
314         if (fileData != null) {
315             String colName = getColumnName(col);
316             // Don't know if it will be useful, but the switch below allows the
317             // column to be in any order.
318             if (colName.equals(this.COL_NAME)) {
319                 return fileData.getFileName();
320             } else if (colName.equals(this.COL_SIZE)) {
321                 return Long.valueOf(fileData.getFileLength());
322             } else if (colName.equals(this.COL_DIRECTORY)) {
323                 return fileData.getDirectory();
324             } else if (colName.equals(this.COL_MODIFIED)) {
325                 return fileData.getLastModified();
326             } else if (colName.equals(this.COL_CHECKED)) {
327                 return fileData.getUploadFlag();
328             } else {
329                 this.uploadPolicy.displayErr("Unknown column in " + this.getClass().getName() + ": " + colName);
330                 return null;
331             }
332         } else {
333             return null;
334         }
335     }
336 
337     /**
338      * This method doesn't do anything : no changeable values.
339      * 
340      * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
341      */
342     @Override
343     public void setValueAt(Object aValue, int row, int col) {
344         if (!columnEditable[col]) {
345             this.uploadPolicy.displayWarn(this.getClass().getName() + ".setValueAt: no action");
346         } else {
347             FileData fd = getFileDataAt(row);
348             if (!(aValue instanceof Boolean)) {
349                 this.uploadPolicy.displayErr("Internal error in " + this.getClass().getName()
350                         + ": o should be a Boolean but is a " + aValue.getClass().getName());
351             } else {
352                 fd.setUploadFlag((Boolean) aValue);
353             }
354         }
355     }
356 
357     /**
358      * @see javax.swing.table.TableModel#getColumnName(int)
359      */
360     @Override
361     public String getColumnName(int arg0) {
362         return this.columnNames[arg0];
363     }
364 
365     /**
366      * Retrieves the default colum percentage size of a column, that is: its percentage of the available width.
367      * 
368      * @param col The index of the column to query.
369      * @return the default size of the requested column.
370      */
371     public int getColumnSizePercentage(int col) {
372         return this.columnSizePercentage[col];
373     }
374 
375     /**
376      * Get the common Root of all files in the current list. Only 'checked' files (uploadFlag to true) are taken into
377      * account.
378      * 
379      * @return
380      */
381     public File getFileRoot() {
382         return DefaultFileData.getRoot(rows);
383     }
384 
385     public List<FileData> getFiles() {
386         return rows;
387     }
388 }