Coverage Report - wjhk.jupload2.filedata.DefaultFileData
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultFileData
0 %
0/138
0 %
0/67
3,08
DefaultFileData$1
100 %
1/1
N/A
3,08
 
 1  
 //
 2  
 // $Id: DefaultFileData.java 267 2007-06-08 13:42:02 +0000 (ven., 08 juin 2007)
 3  
 // felfert $
 4  
 //
 5  
 // jupload - A file upload applet.
 6  
 // Copyright 2007 The JUpload Team
 7  
 //
 8  
 // Created: 2006-04-21
 9  
 // Creator: etienne_sf
 10  
 // Last modified: $Date: 2015-03-29 19:48:44 +0200 (dim., 29 mars 2015) $
 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.filedata;
 23  
 
 24  
 import java.io.File;
 25  
 import java.io.FileInputStream;
 26  
 import java.io.FileNotFoundException;
 27  
 import java.io.IOException;
 28  
 import java.io.InputStream;
 29  
 import java.security.MessageDigest;
 30  
 import java.security.NoSuchAlgorithmException;
 31  
 import java.text.SimpleDateFormat;
 32  
 import java.util.Date;
 33  
 import java.util.List;
 34  
 
 35  
 import javax.swing.tree.TreePath;
 36  
 
 37  
 import wjhk.jupload2.exception.JUploadException;
 38  
 import wjhk.jupload2.exception.JUploadExceptionTooBigFile;
 39  
 import wjhk.jupload2.exception.JUploadIOException;
 40  
 import wjhk.jupload2.gui.filepanel.treeview.TreeFileDataNode;
 41  
 import wjhk.jupload2.policies.DefaultUploadPolicy;
 42  
 import wjhk.jupload2.policies.UploadPolicy;
 43  
 import wjhk.jupload2.upload.helper.ByteArrayEncoder;
 44  
 
 45  
 /**
 46  
  * This class contains all data and methods for a file to upload. The current *
 47  
  * {@link wjhk.jupload2.policies.UploadPolicy} contains the necessary parameters * to personalize the way files must be
 48  
  * handled. <BR>
 49  
  * <BR>
 50  
  * This class is the default FileData implementation. It gives the default behavior, and is used by
 51  
  * {@link DefaultUploadPolicy}. It provides standard control on the files chosen for upload.
 52  
  * 
 53  
  * @see FileData
 54  
  * @author etienne_sf
 55  
  */
 56  
 public class DefaultFileData implements FileData {
 57  
 
 58  
     /** The current upload policy. */
 59  
     UploadPolicy uploadPolicy;
 60  
 
 61  
     /**
 62  
      * Indicates whether the file is prepared for upload or not.
 63  
      * 
 64  
      * @see FileData#isPreparedForUpload()
 65  
      */
 66  0
     boolean preparedForUpload = false;
 67  
 
 68  
     private final static int BUFLEN = 4096;
 69  
 
 70  
     // ///////////////////////////////////////////////////////////////////////////////////////////////////////
 71  
     // /////////////////////// Protected attributes
 72  
     // /////////////////////////////////////////////////////
 73  
     // ///////////////////////////////////////////////////////////////////////////////////////////////////////
 74  
 
 75  
     /**
 76  
      * Mime type of the file. It will be written in the upload HTTP request.
 77  
      */
 78  0
     protected String mimeType = "application/octet-stream";
 79  
 
 80  
     // ///////////////////////////////////////////////////////////////////////////////////////////////////////
 81  
     // /////////////////////// Private attributes
 82  
     // ////////////////////////////////////////////////////////
 83  
     // ///////////////////////////////////////////////////////////////////////////////////////////////////////
 84  
 
 85  
     /** Association with the hierarchical node associated with this file data */
 86  0
     TreeFileDataNode treeFileDataNode = null;
 87  
 
 88  
     /**
 89  
      * file is the file about which this FileData contains data.
 90  
      */
 91  
     protected File file;
 92  
 
 93  
     /**
 94  
      * Cached file size
 95  
      */
 96  
     protected long fileSize;
 97  
 
 98  
     /**
 99  
      * Cached file directory
 100  
      */
 101  
     protected String fileDir;
 102  
 
 103  
     /**
 104  
      * cached root of this file. This root is set during the prepareUpload call to set the root for the uploaded files,
 105  
      * when it is known.
 106  
      */
 107  0
     protected String fileRoot = null;
 108  
 
 109  
     /**
 110  
      * Cached file modification time.
 111  
      */
 112  
     protected Date fileModified;
 113  
 
 114  
     /**
 115  
      * Indicated whether this file should be uploaded or not. Default value is true.
 116  
      */
 117  0
     protected boolean uploadFlag = true;
 118  
 
 119  
     /**
 120  
      * The md5sum for the prepared file. Calculated in the {@link #beforeUpload()}, and cleared in the
 121  
      * {@link #afterUpload()}.
 122  
      */
 123  0
     protected String md5sum = null;
 124  
 
 125  
     /**
 126  
      * Indicates whether the applet can read this file or not.
 127  
      */
 128  0
     protected Boolean canRead = null;
 129  
 
 130  
     /**
 131  
      * Standard constructor
 132  
      * 
 133  
      * @param file The file whose data this instance will give.
 134  
      * @param absoluteRoot The directory root, to be able to calculate the result of {@link #getRelativeDir()}
 135  
      * @param uploadPolicyParam The current upload policy.
 136  
      */
 137  0
     public DefaultFileData(File file, UploadPolicy uploadPolicyParam) {
 138  0
         if (file.isDirectory()) {
 139  0
             throw new IllegalArgumentException(
 140  
                     "Internal Error: DefaultFileData can't be created from a Directory. It needs a file");
 141  
         }
 142  0
         uploadPolicyParam.displayDebug("Creation of the DefaultFileData for " + file.getAbsolutePath(), 10);
 143  0
         this.file = file;
 144  0
         uploadPolicy = uploadPolicyParam;
 145  0
         this.fileSize = this.file.length();
 146  0
         this.fileDir = this.file.getAbsoluteFile().getParent();
 147  0
         this.fileModified = new Date(this.file.lastModified());
 148  0
         this.mimeType = uploadPolicy.getContext().getMimeType(getFileExtension());
 149  0
     }
 150  
 
 151  
     /** {@inheritDoc} */
 152  
     public void appendFileProperties(ByteArrayEncoder bae, int index) throws JUploadIOException {
 153  0
         bae.appendTextProperty("mimetype", getMimeType(), index);
 154  0
         bae.appendTextProperty("pathinfo", getDirectory(), index);
 155  0
         bae.appendTextProperty("relpathinfo", getRelativeDir(), index);
 156  
         // To add the file date/time, we first have to format this date.
 157  0
         SimpleDateFormat dateformat = new SimpleDateFormat(uploadPolicy.getDateFormat());
 158  0
         String uploadFileModificationDate = dateformat.format(getLastModified());
 159  0
         bae.appendTextProperty("filemodificationdate", uploadFileModificationDate, index);
 160  0
     }
 161  
 
 162  
     /** {@inheritDoc} */
 163  
     public synchronized void beforeUpload(String uploadFileRoot) throws JUploadException {
 164  0
         if (this.preparedForUpload) {
 165  
             // Maybe an upload was stopped. Let's log a resume, and resume the
 166  
             // job.
 167  0
             uploadPolicy.displayWarn("The file " + getFileName() + " is already prepared for upload");
 168  
         } else {
 169  
             // The file is now prepared for upload.
 170  0
             this.preparedForUpload = true;
 171  
 
 172  0
             this.fileRoot = uploadFileRoot;
 173  
 
 174  
             // Should we calculate the MD5Sum for this file ?
 175  0
             if (uploadPolicy.getSendMD5Sum()) {
 176  0
                 calculateMD5Sum();
 177  
             }
 178  
 
 179  
             // Default : we check that the file is smaller than the maximum
 180  
             // upload size.
 181  0
             if (getUploadLength() > uploadPolicy.getMaxFileSize()) {
 182  0
                 throw new JUploadExceptionTooBigFile(getFileName(), getUploadLength(), uploadPolicy);
 183  
             }
 184  
         }
 185  0
     }
 186  
 
 187  
     /** {@inheritDoc} */
 188  
     public long getUploadLength() {
 189  0
         if (!this.preparedForUpload) {
 190  0
             throw new IllegalStateException("The file " + getFileName() + " is not prepared for upload");
 191  
         }
 192  0
         return this.fileSize;
 193  
     }
 194  
 
 195  
     /** {@inheritDoc} */
 196  
     public synchronized void afterUpload() {
 197  0
         if (!this.preparedForUpload) {
 198  0
             throw new IllegalStateException("The file " + getFileName() + " is not prepared for upload");
 199  
         }
 200  
         // Let's free resources or temporary calculation in DefaultFileData
 201  0
         this.md5sum = null;
 202  
 
 203  
         // Then, we change the preparation status.
 204  0
         this.preparedForUpload = false;
 205  0
     }
 206  
 
 207  
     /** {@inheritDoc} */
 208  
     public synchronized InputStream getInputStream() throws JUploadException {
 209  0
         if (!this.preparedForUpload) {
 210  0
             throw new IllegalStateException("The file " + getFileName() + " is not prepared for upload");
 211  
         }
 212  
         // Standard FileData : we read the file.
 213  
         try {
 214  0
             return new FileInputStream(this.file);
 215  0
         } catch (FileNotFoundException e) {
 216  0
             throw new JUploadIOException(e);
 217  
         }
 218  
     }
 219  
 
 220  
     /** {@inheritDoc} */
 221  
     public String getFileName() {
 222  0
         return this.file.getName();
 223  
     }
 224  
 
 225  
     /** {@inheritDoc} */
 226  
     public String getFileExtension() {
 227  0
         return getExtension(getFileName());
 228  
     }
 229  
 
 230  
     /** {@inheritDoc} */
 231  
     public long getFileLength() {
 232  0
         return this.fileSize;
 233  
     }
 234  
 
 235  
     /** {@inheritDoc} */
 236  
     public Date getLastModified() {
 237  0
         return this.fileModified;
 238  
     }
 239  
 
 240  
     /** {@inheritDoc} */
 241  
     public String getDirectory() {
 242  0
         return this.fileDir;
 243  
     }
 244  
 
 245  
     /** {@inheritDoc} */
 246  
     public boolean getUploadFlag() {
 247  0
         return this.uploadFlag;
 248  
     }
 249  
 
 250  
     /** {@inheritDoc} */
 251  
     public void setUploadFlag(boolean uploadFlag) {
 252  0
         this.uploadFlag = uploadFlag;
 253  0
     }
 254  
 
 255  
     /** {@inheritDoc} */
 256  
     public String getMD5() throws JUploadException {
 257  0
         if (this.md5sum == null) {
 258  0
             throw new JUploadException("The MD5Sum has not been calculated!");
 259  
         }
 260  0
         return this.md5sum;
 261  
     }
 262  
 
 263  
     /**
 264  
      * Calculate the MD5Sum for the transformed file, or the original if no transformation should be done on the file,
 265  
      * before upload.
 266  
      * 
 267  
      * @throws JUploadException
 268  
      */
 269  
     public void calculateMD5Sum() throws JUploadException {
 270  0
         StringBuffer ret = new StringBuffer();
 271  0
         MessageDigest digest = null;
 272  0
         byte md5Buffer[] = new byte[BUFLEN];
 273  
         int nbBytes;
 274  
 
 275  
         // Calculation of the MD5 sum. Now done before upload, to prepare the
 276  
         // file head.
 277  
         // This makes the file being parsed two times: once before upload, and
 278  
         // once for the actual upload
 279  0
         InputStream md5InputStream = getInputStream();
 280  
         try {
 281  0
             digest = MessageDigest.getInstance("MD5");
 282  0
             while ((nbBytes = md5InputStream.read(md5Buffer, 0, BUFLEN)) > 0) {
 283  0
                 digest.update(md5Buffer, 0, nbBytes);
 284  
             }
 285  0
         } catch (IOException e) {
 286  0
             throw new JUploadIOException(e);
 287  0
         } catch (NoSuchAlgorithmException e) {
 288  0
             throw new JUploadException(e);
 289  
         } finally {
 290  0
             try {
 291  0
                 md5InputStream.close();
 292  0
             } catch (IOException e) {
 293  0
                 throw new JUploadIOException(e);
 294  0
             }
 295  0
         }
 296  
 
 297  
         // Now properly format the md5 sum.
 298  0
         byte md5sum[] = new byte[32];
 299  0
         if (digest != null)
 300  0
             md5sum = digest.digest();
 301  0
         for (int i = 0; i < md5sum.length; i++) {
 302  0
             ret.append(Integer.toHexString((md5sum[i] >> 4) & 0x0f));
 303  0
             ret.append(Integer.toHexString(md5sum[i] & 0x0f));
 304  
         }
 305  
 
 306  0
         this.md5sum = ret.toString();
 307  0
     }
 308  
 
 309  
     /** {@inheritDoc} */
 310  
     public String getMimeType() {
 311  0
         return this.mimeType;
 312  
     }
 313  
 
 314  
     /** {@inheritDoc} */
 315  
     public boolean canRead() {
 316  
         // The commented line below doesn't seems to work.
 317  
         // return this.file.canRead();
 318  
 
 319  
         // The canRead status is read once. This is done in this method, so that
 320  
         // it's available for all subclasses. If it were in the constructor, we
 321  
         // would have to initialize {@link #canRead} in all subclasses.
 322  
 
 323  
         // Let's store the status 'readible' only once. It's
 324  0
         if (this.canRead == null) {
 325  
             try {
 326  0
                 InputStream is = new FileInputStream(this.file);
 327  0
                 is.close();
 328  0
                 this.canRead = Boolean.valueOf(true);
 329  0
             } catch (IOException e) {
 330  
                 // Can't read the file!
 331  0
                 this.canRead = Boolean.valueOf(false);
 332  0
             }
 333  
         }
 334  
 
 335  0
         return this.canRead.booleanValue();
 336  
     }
 337  
 
 338  
     /**
 339  
      * Standard getter, for the file described by the FileData instance.<BR/>
 340  
      * Since JUpload 6.0, this method is no more in the {@link FileData} interface. Direct link between the DefaultFile
 341  
      * and the File should be avoided. This is especially true to access the file path. You should the other method of
 342  
      * this interface, instead of this one. Typically, using this method is incompatible with the
 343  
      * {@link TreeViewUploadPolicy}<BR/>
 344  
      * To check if a file is already in the current upload list, you should now use the {@link #getAbsolutePath()}
 345  
      * method.
 346  
      * 
 347  
      * @return the File instance associated with this row.
 348  
      */
 349  
     protected File getFile() {
 350  0
         return this.file;
 351  
     }
 352  
 
 353  
     /** {@inheritDoc} */
 354  
     public String getRelativeDir() {
 355  0
         String ret = "";
 356  1
         switch (this.uploadPolicy.getFileListViewMode()) {
 357  
             case FLAT:
 358  
             case TREE_VIEW:
 359  0
                 if (null == this.fileRoot || this.fileRoot.equals("")) {
 360  
                     // No common root. The relative dir is the whole path for the current DefaultFileData
 361  0
                     ret = this.fileDir;
 362  0
                 } else if (this.fileDir.startsWith(this.fileRoot)) {
 363  0
                     int skip = this.fileRoot.length();
 364  0
                     if (this.fileRoot.endsWith(File.separator))
 365  0
                         skip++;
 366  0
                     if ((skip >= 0) && (skip < this.fileDir.length()))
 367  0
                         ret = this.fileDir.substring(skip);
 368  0
                 } else {
 369  
                     // Too bad, this should not happen
 370  0
                     throw new IllegalStateException("Root (" + this.fileRoot + ") is not part of current path ("
 371  
                             + this.fileDir + ")");
 372  
                 }
 373  
                 break;
 374  
             case INDEPENDENT_TREE_VIEW:
 375  0
                 TreePath treePath = treeFileDataNode.getTreePath();
 376  
                 // Get through all parents of this node (but skip this node)
 377  0
                 for (int i = 0; i < treePath.getPathCount() - 1; i += 1) {
 378  0
                     String nodeName = ((TreeFileDataNode) treePath.getPathComponent(i)).toString();
 379  0
                     if (!nodeName.equals("")) {
 380  0
                         ret += (ret.equals("") ? "" : "/") + nodeName;
 381  
                     }
 382  
                 }// for
 383  
         }
 384  
 
 385  0
         return ret;
 386  
     }
 387  
 
 388  
     /** {@inheritDoc} */
 389  
     public String getAbsolutePath() {
 390  0
         return file.getAbsolutePath();
 391  
     }
 392  
 
 393  
     // ////////////////////////////////////////////////////////
 394  
     // UTILITIES
 395  
     // ////////////////////////////////////////////////////////
 396  
     /**
 397  
      * Returns the extension of the given file. To be clear: <I>jpg</I> is the extension for the file named
 398  
      * <I>picture.jpg</I>.
 399  
      * 
 400  
      * @param file the file whose the extension is wanted!
 401  
      * @return The extension, without the point, for the given file.
 402  
      */
 403  
     public static String getExtension(String filename) {
 404  0
         return filename.substring(filename.lastIndexOf('.') + 1);
 405  
     }
 406  
 
 407  
     /**
 408  
      * Return the 'biggest' common ancestror of the given file array. For instance, the root for the files /usr/bin/toto
 409  
      * and /usr/titi is /usr.
 410  
      * 
 411  
      * @param rows
 412  
      * @return The common root for the given files.
 413  
      */
 414  
     public static File getRoot(List<? extends FileData> rows) {
 415  
         // Let's find the common root for the dropped files.
 416  
         // If one file has been dropped (the minimum), the path of its parent should be the root.
 417  0
         File root = ((DefaultFileData) rows.get(0)).file;
 418  0
         if (root.isDirectory()) {
 419  0
             root = root.getParentFile();
 420  
         }
 421  
         // Let's find the higher root level.
 422  0
         while (root != null && !root.isDirectory()) {
 423  
             // We have a file. Let's take it's folder.
 424  0
             root = root.getParentFile();
 425  
         }
 426  
 
 427  0
         if (root != null) {
 428  
             // root is the root for the first file. We add all directories, and higher until the root. This will allow
 429  
             // to find the 'bigger' directory, which is the common root for all dropped files. If several files are
 430  
             // being added, we take the common root for them.
 431  0
             String pathRoot = root.getAbsolutePath() + File.separator;
 432  
             File pathCurrentFileParent;
 433  
 
 434  
             // We start with the second item in the list, as we already
 435  
             // extracted the first.
 436  0
             for (int i = 1; i < rows.size() && root != null; i += 1) {
 437  
                 // We ignore the unflagged files.
 438  0
                 if (rows.get(i).getUploadFlag()) {
 439  
                     // Let's check that all files in l are parents of the current file.
 440  0
                     pathCurrentFileParent = ((DefaultFileData) rows.get(i)).file;
 441  0
                     String pathCurrentFileParentPath = pathCurrentFileParent.getAbsolutePath() + File.separator;
 442  
 
 443  
                     // We loop through the parent of the file, until we find a
 444  
                     // common root.
 445  
                     do {
 446  0
                         pathCurrentFileParent = pathCurrentFileParent.getParentFile();
 447  0
                         pathCurrentFileParentPath = (pathCurrentFileParent == null) ? "" : pathCurrentFileParent
 448  0
                                 .getAbsolutePath() + File.separator;
 449  0
                     } while (pathCurrentFileParent != null && !pathRoot.startsWith(pathCurrentFileParentPath));
 450  
 
 451  
                     // Let's store the new found root (which may actually be the
 452  
                     // same as the last one)
 453  0
                     root = pathCurrentFileParent;
 454  0
                     pathRoot = pathCurrentFileParentPath;
 455  
                 }// if
 456  
             }// for
 457  
 
 458  
             // pathRoot contains the path for the found root.
 459  0
             if (pathRoot.equals("")) {
 460  
                 // No common root (e.g.: on windows, adding C:\temp and D:\temp
 461  0
                 root = null;
 462  
             } else {
 463  0
                 root = new File(pathRoot);
 464  
             }
 465  
         }
 466  
 
 467  0
         return root;
 468  
     }
 469  
 
 470  
     /** {@inheritDoc} */
 471  
     public boolean isPreparedForUpload() {
 472  0
         return this.preparedForUpload;
 473  
     }
 474  
 
 475  
     /**
 476  
      * @return the treeFileDataNode
 477  
      */
 478  
     public TreeFileDataNode getTreeFileDataNode() {
 479  0
         return treeFileDataNode;
 480  
     }
 481  
 
 482  
     /** {@inheritDoc} */
 483  
     public void setTreeFileDataNode(TreeFileDataNode treeFileDataNode) {
 484  0
         this.treeFileDataNode = treeFileDataNode;
 485  0
     }
 486  
 
 487  
 }