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 }