Changeset 3641


Ignore:
Timestamp:
Aug 8, 2007, 1:32:52 PM (14 years ago)
Author:
Nicklas Nordborg
Message:

References #451: Zipped download

Major redesign of the packing plug-in structure.

Location:
trunk
Files:
2 added
4 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/data/plugin_configfile.xml

    r3091 r3641  
    881881    </parameter>
    882882  </configuration>
     883  <configuration pluginClassName="net.sf.basedb.plugins.PackedFileExporter">
     884    <configname>Zip archive (.zip)</configname>
     885    <description>Compress the selected files/directories and put them in a ZIP file.</description>
     886    <parameter>
     887      <name>packer</name>
     888      <label>Packer class</label>
     889      <description>Enter the name of the class that is responsible for packing the files. It must be a class that implements the FilePacker interface.</description>
     890      <class>java.lang.String</class>
     891      <value>net.sf.basedb.util.zip.ZipFilePacker</value>
     892    </parameter>
     893  </configuration>
     894  <configuration pluginClassName="net.sf.basedb.plugins.PackedFileExporter">
     895    <configname>TAR archive (.tar)</configname>
     896    <description>Collect the selected files/directories into a TAR file (not compressed).</description>
     897    <parameter>
     898      <name>packer</name>
     899      <label>Packer class</label>
     900      <description>Enter the name of the class that is responsible for packing the files. It must be a class that implements the FilePacker interface.</description>
     901      <class>java.lang.String</class>
     902      <value>net.sf.basedb.util.zip.TarFilePacker</value>
     903    </parameter>
     904  </configuration>
     905  <configuration pluginClassName="net.sf.basedb.plugins.PackedFileExporter">
     906    <configname>GZipped TAR archive (.tar.gz)</configname>
     907    <description>Collect the selected files/directoris into a TAR file and compress it with GZIP.</description>
     908    <parameter>
     909      <name>packer</name>
     910      <label>Packer class</label>
     911      <description>Enter the name of the class that is responsible for packing the files. It must be a class that implements the FilePacker interface.</description>
     912      <class>java.lang.String</class>
     913      <value>net.sf.basedb.util.zip.GzipFilePacker</value>
     914    </parameter>
     915  </configuration>
     916  <configuration pluginClassName="net.sf.basedb.plugins.PackedFileExporter">
     917    <configname>BZipped TAR archive (.tar.bz2)</configname>
     918    <description>Collect the selected files/directoris into a TAR file and compress it with BZIP2.</description>
     919    <parameter>
     920      <name>packer</name>
     921      <label>Packer class</label>
     922      <description>Enter the name of the class that is responsible for packing the files. It must be a class that implements the FilePacker interface.</description>
     923      <class>java.lang.String</class>
     924      <value>net.sf.basedb.util.zip.Bzip2FilePacker</value>
     925    </parameter>
     926  </configuration>
    883927</configfile>
  • trunk/src/core/net/sf/basedb/core/plugin/AbstractExporterPlugin.java

    r3011 r3641  
    7474 
    7575  protected static final BooleanParameterType overwriteType =
    76     new BooleanParameterType(false, false);
     76    new BooleanParameterType(null, false);
    7777 
    7878  /**
  • trunk/src/core/net/sf/basedb/util/zip/FilePacker.java

    r3568 r3641  
    2525package net.sf.basedb.util.zip;
    2626
    27 import net.sf.basedb.core.AbsoluteProgressReporter;
    28 import net.sf.basedb.core.DbControl;
    29 
    3027import java.io.IOException;
     28import java.io.InputStream;
    3129import java.io.OutputStream;
    32 import java.util.List;
    3330
    3431/**
    35   This interface should be implemented by classes that pack files and directories
    36   from BASE2 file system and are used by the plugin {@link net.sf.basedb.plugins.PackedFileExporter}.
    37     @author Martin
     32  This interface should be implemented by classes that can pack files and directories
     33  from BASE2 file system. The implementing class must also have a public no-argument
     34  constructor. The implementing classes are used by the 
     35  {@link net.sf.basedb.plugins.PackedFileExporter} plug-in. Each class should
     36  be registered in a {@link net.sf.basedb.core.PluginConfiguration} for the
     37  <code>PackedFileExporter</code>.
     38 
     39    @author Martin, Nicklas
    3840    @version 2.4   
    3941    @base.modified $Date: 2006-11-30 10:50:05 +0100 (Thu, 30 Nov 2006) $
    40  */
     42*/
    4143public interface FilePacker
    4244{
     45 
    4346  /**
    44     Gets the extension that files, packed with this tool, should have.
    45       @return a String
     47    Get a short description of this file format that is suitable for display in
     48    selection list boxes.
     49  */
     50  public String getDescription();
     51 
     52  /**
     53    Gets the extension that files, packed with this tool, should have,
     54    for example, <code>zip</code> or <code>tar.gz</code> Don't include the first
     55    dot!
     56      @return A String with the file extension excluding the dot
    4657  */
    4758  public String getFileExtension();
    4859 
    4960  /**
    50     Gets the mimetype to give compressed files from this packer
    51       @return a String
    52    */
     61    Gets the MIME type to give compressed files from this packer
     62      @return The MIME type of the compressed file format
     63  */
    5364  public String getMimeType();
    5465 
    5566  /**
    56     Sets the flag if the selected files and directories should be removed after they
    57     have been packed
    58       @param remove TRUE if items should be removed, FALSE otherwise
    59    */
    60   public void setRemoveAfter(boolean remove);
     67    The output stream that the compressed files should be written to.
     68    @param out The output stream to write to
     69    @throws IOException If there is an error setting the output stream
     70  */
     71  public void setOutputStream(OutputStream out)
     72    throws IOException;
    6173 
    6274  /**
    63     Packs the files and directories to the output stream and
    64     then returns how many of the selected paths that were packed.
    65       @param out the output stream to pack the files to.
    66       @param filePaths a list with paths to the files that should be included in the packed file
    67       @param directoryPaths Paths to directories that should be included in the packed file
    68       @param progress A progress reporter to display how the packer is proceeding.
    69       @return int How many of the selected paths that have been packed
    70       @throws IOException if there are any errors when reading/writing to the files.
    71    */
    72   public int pack(DbControl dc, OutputStream out, List<String> filePaths,
    73       List<String> directoryPaths, AbsoluteProgressReporter progress)
     75    Compress the uncompressed input stream to an entry with the given
     76    name into the outputstream.
     77    @param entryName The name of the packed resource
     78      @param in The input stream to read uncompressed data from, or null
     79        if the entry represents a directory
     80      @param size The number of bytes of uncompressed data
     81      @param lastModified The time the contents was last modified, or 0
     82        if not known
     83      @throws IOException If there are any errors when reading or writing
     84  */
     85  public void pack(String entryName, InputStream in, long size, long lastModified)
     86    throws IOException;
     87 
     88  /**
     89    End the packing and close. The plug-in should close any open resources and
     90    cleanup temporary objects. This method should not close the output stream
     91    set by the {@link #setOutputStream(OutputStream)} method, since this is
     92    the responsibility of the calling code.
     93   
     94      @throws IOException If there are any errors
     95  */
     96  public void close()
    7497    throws IOException;
    7598}
  • trunk/src/core/net/sf/basedb/util/zip/TarFilePacker.java

    r3637 r3641  
    2323*/
    2424
    25 package net.sf.basedb.plugins;
     25package net.sf.basedb.util.zip;
    2626
    27 import net.sf.basedb.core.AbsoluteProgressReporter;
    28 import net.sf.basedb.core.BaseException;
    29 import net.sf.basedb.core.DbControl;
    30 import net.sf.basedb.core.Directory;
    31 import net.sf.basedb.core.File;
    32 import net.sf.basedb.core.ItemResultList;
    33 import net.sf.basedb.core.Path;
    3427import net.sf.basedb.util.FileUtil;
    35 import net.sf.basedb.util.zip.FilePacker;
    3628
    37 import java.io.FileInputStream;
    38 import java.io.FileOutputStream;
    3929import java.io.IOException;
     30import java.io.InputStream;
    4031import java.io.OutputStream;
    41 import java.util.List;
    42 import java.util.zip.GZIPOutputStream;
    43 
    44 import org.apache.tools.bzip2.CBZip2OutputStream;
    4532
    4633import com.ice.tar.TarEntry;
    4734import com.ice.tar.TarOutputStream;
    4835
    49 
    5036/**
    51   This class packs files into GZipped or BZipped Tar-format.
    52     @author martin
     37  This class packs files into a TAR archive.
     38    @author martin, Nicklas
    5339    @version 2.4
    5440    @base.modified $Date: 2007-07-11 15:45:01 +0200 (Mon, 02 Jul 2007) $   
    55  */
     41*/
    5642public class TarFilePacker
    5743  implements FilePacker
    5844{
    59   private static final String MIME_TYPE_GZIP = "application/x-gzip";
    60   private static final String MIME_TYPE_BZIP = "applicaton/x-bzip2";
    61   private static final String EXTENSION_GZIP = "tar.gz";
    62   private static final String EXTENSION_BZIP = "tar.bz2";
    6345 
    64   private boolean removeAfter = false;
    65   private boolean useBzip2 = false;
     46  private TarOutputStream tar;
    6647 
    6748  /**
    6849    Creates a new TarFilePacker.
    69       @param packWithBzip2 TRUE if the tar-file should be compressed with BZip2,
    70         FALSE if the tar-file should be compressed with GZip.
    7150  */
    72   public TarFilePacker(boolean packWithBzip2)
    73   {
    74     this.useBzip2 = packWithBzip2;
    75   }
     51  public TarFilePacker()
     52  {}
    7653 
    7754  /*
    78       @see net.sf.basedb.util.zip.FilePacker#getFileExtension()
     55    From the FilePacker interface
     56    -------------------------------------------
     57  */
     58  /**
     59    @return "Tar-archive (.tar)"
     60  */
     61  public String getDescription()
     62  {
     63    return "Tar-archive (.tar)";
     64  }
     65  /**
     66    @return "tar"
    7967  */
    8068  public String getFileExtension()
    8169  {
    82     return useBzip2 ? EXTENSION_BZIP : EXTENSION_GZIP;
     70    return "tar";
    8371  }
    84 
    85   /*
    86       @see net.sf.basedb.util.zip.FilePacker#getMimeType()
     72  /**
     73    @return "application/x-tar"
    8774  */
    8875  public String getMimeType()
    8976  {
    90     return useBzip2 ? MIME_TYPE_BZIP : MIME_TYPE_GZIP;
     77    return "application/x-tar";
    9178  }
    92   /*
    93       @see net.sf.basedb.util.zip.FilePacker#setRemoveAfter(boolean)
     79  /**
     80   * Wrap the output stream in a {@link TarOutputStream}.
     81   */
     82  public void setOutputStream(OutputStream out)
     83    throws IOException
     84  {
     85    this.tar = new TarOutputStream(out);
     86  }
     87  /**
     88    Create a new {@link TarEntry} and write the compressed
     89    data to it.
    9490  */
    95   public void setRemoveAfter(boolean remove)
     91  public void pack(String entryName, InputStream in, long size, long lastModified)
     92    throws IOException
    9693  {
    97     removeAfter = remove;
     94    boolean isDirectory = in == null;
     95    if (isDirectory && !entryName.endsWith("/")) entryName += "/";
     96    TarEntry entry = new TarEntry(entryName);
     97    if (lastModified > 0) entry.setModTime(lastModified);
     98    entry.setSize(size);
     99    tar.putNextEntry(entry);
     100    if (!isDirectory) FileUtil.copy(in, tar);
     101    tar.flush();
     102    tar.closeEntry();
    98103  }
    99 
    100   /*
    101       @see net.sf.basedb.util.zip.FilePacker#pack(net.sf.basedb.core.DbControl, java.io.OutputStream, java.util.List, java.util.List)
     104  /**
     105    Finish the TAR file.
    102106  */
    103   public int pack(DbControl dc, OutputStream out, List<String> filePaths,
    104       List<String> directoryPaths, AbsoluteProgressReporter progress) throws IOException
     107  public void close()
     108    throws IOException
    105109  {
    106     int compressedItems = 0;
    107     java.io.File tarFile = new java.io.File("temp.tar");
    108    
    109     // Create the tar-file
    110     TarOutputStream tout = new TarOutputStream(new FileOutputStream(tarFile));   
    111     for (String pathString : filePaths)
    112     {
    113       File baseFile = File.getByPath(dc, new Path(pathString, Path.Type.FILE), false);
    114       if (progress != null)
    115       {
    116         progress.displayAbsolute(compressedItems, "Packing file: " + baseFile.getName());
    117       }
    118       packFileToTar(tout, baseFile, null);
    119       baseFile.setRemoved(removeAfter ? true : baseFile.isRemoved());
    120       compressedItems++;
    121     }
    122    
    123     for (String pathString : directoryPaths)
    124     {
    125       Directory dir = Directory.getByPath(dc, new Path(pathString, Path.Type.DIRECTORY));
    126       if (progress != null)
    127       {
    128         progress.displayAbsolute(compressedItems, "Packing directory: " + dir.getName());
    129       }
    130       packDirectoryToTar(dc, tout, dir);
    131       dir.setRemoved(removeAfter ? true : dir.isRemoved());
    132       compressedItems++;
    133     }
    134     tout.flush();
    135     tout.close();
    136    
    137     //Compress the tar-file
    138     OutputStream zipOut;
    139     if (useBzip2)
    140     {
    141       out.write('B');
    142       out.write('Z');
    143       zipOut = new CBZip2OutputStream(out);   
    144     }
    145     else
    146     {
    147       zipOut = new GZIPOutputStream(out);
    148     }   
    149     FileUtil.copy(new FileInputStream(tarFile), zipOut);
    150     zipOut.flush();
    151     zipOut.close();
    152 
    153     return compressedItems;
     110    tar.finish();
     111    tar = null;
    154112  }
    155 
    156   /**
    157     Compress a directory to a tar outputstream
    158     This method will call itself for each subfolder
    159     and add it recursively.
    160       @param tout the TarOutputStream for the target file
    161       @param dir the Directory to pack
    162   */
    163   private void packDirectoryToTar(DbControl dc, TarOutputStream tout, Directory dir)
    164   {
    165     ItemResultList<File> files = dir.getFiles().list(dc);
    166     ItemResultList<Directory> subDirectories = dir.getSubDirectories().list(dc);
    167    
    168     String parentPath = dir.getParent().getPath().toString();
    169     for (File file : files)
    170     {
    171       String filePath = file.getPath().toString();
    172       String relFilePath = filePath.substring(parentPath.length());
    173       packFileToTar(tout, file, relFilePath);
    174     }
    175     for (Directory subDir : subDirectories)
    176     {
    177       packDirectoryToTar(dc, tout, subDir);
    178     }
    179   }
    180    
    181   /**
    182     Compress a base file to a tar-file's outputstream.
    183       @param tout the TarOutputStream for the target file
    184       @param file the file that should be packed.
    185   */
    186   private void packFileToTar(TarOutputStream tout, File file, String relativePath)
    187   {
    188     String entry_name = relativePath == null ? file.getName() : relativePath;
    189     if ( entry_name.charAt( 0 ) == java.io.File.separatorChar )
    190     {
    191           entry_name = entry_name.substring( 1 );
    192     }
    193    
    194     TarEntry entry = new TarEntry(entry_name);
    195     entry.setSize(file.getSize());
    196     try
    197     {
    198       tout.putNextEntry(entry);
    199       FileUtil.copy(file.getDownloadStream(0), tout);
    200       tout.flush();
    201       tout.closeEntry();
    202     }
    203     catch (Throwable th)
    204     {     
    205       throw new BaseException(th);
    206     }
    207   }
     113  // -------------------------------------------
    208114
    209115}
  • trunk/src/core/net/sf/basedb/util/zip/ZipFilePacker.java

    r3637 r3641  
    2323*/
    2424
    25 package net.sf.basedb.plugins;
     25package net.sf.basedb.util.zip;
    2626
    27 import net.sf.basedb.core.AbsoluteProgressReporter;
    28 import net.sf.basedb.core.BaseException;
    29 import net.sf.basedb.core.DbControl;
    30 import net.sf.basedb.core.Directory;
    31 import net.sf.basedb.core.File;
    32 import net.sf.basedb.core.ItemResultList;
    33 import net.sf.basedb.core.Path;
    3427import net.sf.basedb.util.FileUtil;
    35 import net.sf.basedb.util.zip.FilePacker;
    3628
    3729import java.io.IOException;
     30import java.io.InputStream;
    3831import java.io.OutputStream;
    39 import java.util.List;
    4032import java.util.zip.ZipEntry;
    4133import java.util.zip.ZipOutputStream;
    4234
     35/**
     36  This class packs files into a ZIP archive.
     37
     38  @author Martin, Nicklas
     39  @version 2.4
     40  @base.modified $Date$
     41*/
    4342public class ZipFilePacker
    4443  implements FilePacker
    4544{
    46   private static final String EXTENSION = "zip";
    47   private static final String MIME_TYPE = "application/zip";
     45  private ZipOutputStream zip;
    4846 
    49   private boolean removeAfter = false;
    50  
     47  /**
     48    Create a new packer that compresses files with the zip format.
     49  */
     50  public ZipFilePacker()
     51  {}
     52
    5153  /*
    52       @see net.sf.basedb.util.zip.FilePacker#setRemoveAfter(boolean)
     54    From the FilePacker interface
     55    -------------------------------------------
    5356  */
    54   public void setRemoveAfter(boolean remove)
     57  /**
     58    @return Always "Zip-archive (.zip)"
     59  */
     60  public String getDescription()
    5561  {
    56     removeAfter = remove;
     62    return "Zip-archive (.zip)";
    5763  }
    58  
    59   /*
    60       @see net.sf.basedb.util.zip.FilePacker#getFileExtension()
     64  /**
     65    @return Always "zip"
    6166  */
    6267  public String getFileExtension()
    6368  {
    64     return EXTENSION;
     69    return "zip";
    6570  }
    66 
    67   /*
    68       @see net.sf.basedb.util.zip.FilePacker#getMimeType()
     71  /**
     72      @return Always "application/zip"
    6973  */
    7074  public String getMimeType()
    7175  {
    72     return MIME_TYPE;
     76    return "application/zip";
    7377  }
    74 
    75   /*
    76       @see net.sf.basedb.util.zip.FilePacker#pack(net.sf.basedb.core.DbControl, java.io.OutputStream, java.util.List, java.util.List)
     78  /**
     79   * Wrap the output stream in a {@link ZipOutputStream}.
     80   */
     81  public void setOutputStream(OutputStream out)
     82  {
     83    this.zip = new ZipOutputStream(out);
     84    zip.setMethod(ZipOutputStream.DEFLATED);
     85  }
     86  /**
     87    Create a new {@link ZipEntry} and write the compressed
     88    data to it.
    7789  */
    78   public int pack(DbControl dc, OutputStream out, List<String> filePaths,
    79       List<String> directoryPaths, AbsoluteProgressReporter progress)
     90  public void pack(String entryName, InputStream in, long size, long lastModified)
    8091    throws IOException
    8192  {
    82     int compressedItems = 0;
    83     ZipOutputStream zipOut = new ZipOutputStream(out);     
    84     zipOut.setMethod(ZipOutputStream.DEFLATED);
    85    
    86     //Zip all the selected files to target output
    87     for (String pathString : filePaths)
    88     {     
    89       File baseFile = File.getByPath(dc, new Path(pathString, Path.Type.FILE), false);
    90       if (progress != null)
    91       {
    92         progress.displayAbsolute(compressedItems, "Packing file: " + baseFile.getName());
    93       }
    94       packFileToZip(zipOut, baseFile, null);
    95      
    96       baseFile.setRemoved(removeAfter ? true : baseFile.isRemoved());
    97       compressedItems++;
     93    boolean isDirectory = in == null;
     94    if (isDirectory && !entryName.endsWith("/")) entryName += "/";
     95    ZipEntry entry = new ZipEntry(entryName);
     96    if (lastModified > 0) entry.setTime(lastModified);
     97    if (isDirectory)
     98    {
     99      entry.setMethod(ZipOutputStream.STORED);
     100      entry.setSize(0);
     101      entry.setCrc(0);
    98102    }
    99    
    100     //Zip all the selected directories to target output
    101     for (String pathString : directoryPaths)
     103    else
    102104    {
    103       Directory dir = Directory.getByPath(dc, new Path (pathString, Path.Type.DIRECTORY));
    104       if (progress != null)
    105       {
    106         progress.displayAbsolute(compressedItems, "Packing directory: " + dir.getName());
    107       }
    108       packDirectoryToZip(dc, zipOut, Directory.getByPath(dc, dir.getPath()));
    109      
    110       dir.setRemoved(removeAfter ? true : dir.isRemoved());
    111       compressedItems++;
    112      
     105      entry.setSize(size);
    113106    }
    114     zipOut.flush();
    115     zipOut.close();
    116     return compressedItems;
    117   }
    118  
    119   /**
    120     Compress a base file to a zip outputstream
    121       @param zipOut The ZipOutputStream to compress the file to
    122       @param file File to compress
    123       @param relativePath Relative path to file from the root of the zip file
    124   */
    125   private void packFileToZip(ZipOutputStream zipOut, File file, String relativePath)
    126   {       
    127     String entry_name = relativePath == null ? file.getName() : relativePath;
    128     if ( entry_name.charAt( 0 ) == java.io.File.separatorChar )
    129     {
    130           entry_name = entry_name.substring( 1 );
    131     }   
    132     ZipEntry entry = new ZipEntry(entry_name);
    133     try
    134     {
    135       zipOut.putNextEntry(entry);
    136       FileUtil.copy(file.getDownloadStream(0), zipOut);     
    137       zipOut.flush();
    138       zipOut.closeEntry();
    139     }
    140     catch (Throwable th)
    141     {
    142       throw new BaseException(th);
    143     }
     107    zip.putNextEntry(entry);
     108    if (!isDirectory) FileUtil.copy(in, zip);
     109    zip.flush();
     110    zip.closeEntry();
    144111  }
    145112  /**
    146     Compress a directory to a zip outputstream.
    147     This method will call itself for each subfolder
    148     and add it recursively.
    149       @param zipOut The ZipOutPutStream to compress the directory to
    150       @param dir The directory that should be compressed
     113    Finish the ZIP file.
    151114  */
    152   private void packDirectoryToZip(DbControl dc, ZipOutputStream zipOut, Directory dir)
     115  public void close()
     116    throws IOException
    153117  {
    154     ItemResultList<File> files = dir.getFiles().list(dc);
    155     ItemResultList<Directory> subDirectories = dir.getSubDirectories().list(dc);
    156        
    157     String parentPath = dir.getParent().getPath().toString();
    158     for (File file : files)
    159     {
    160       String filePath = file.getPath().toString();
    161       String relFilePath = filePath.substring(parentPath.length());
    162       packFileToZip(zipOut, file, relFilePath);
    163     }
    164     for (Directory subDir : subDirectories)
    165     {
    166       packDirectoryToZip(dc, zipOut, subDir);
    167     }   
     118    zip.finish();
     119    zip = null;
    168120  }
     121  // -------------------------------------------
     122
    169123}
  • trunk/src/plugins/core/net/sf/basedb/plugins/PackedFileExporter.java

    r3616 r3641  
    2525package net.sf.basedb.plugins;
    2626
    27 import net.sf.basedb.core.AbsoluteProgressReporter;
    2827import net.sf.basedb.core.BaseException;
    2928import net.sf.basedb.core.BooleanParameterType;
     
    3130import net.sf.basedb.core.Directory;
    3231import net.sf.basedb.core.File;
     32import net.sf.basedb.core.Include;
     33import net.sf.basedb.core.IntegerParameterType;
    3334import net.sf.basedb.core.Item;
     35import net.sf.basedb.core.ItemContext;
     36import net.sf.basedb.core.ItemParameterType;
     37import net.sf.basedb.core.ItemQuery;
     38import net.sf.basedb.core.Location;
     39import net.sf.basedb.core.Nameable;
    3440import net.sf.basedb.core.ParameterType;
    35 import net.sf.basedb.core.Path;
    36 import net.sf.basedb.core.PathParameterType;
    3741import net.sf.basedb.core.Permission;
    3842import net.sf.basedb.core.PluginDefinition;
     
    4044import net.sf.basedb.core.ProgressReporter;
    4145import net.sf.basedb.core.RequestInformation;
    42 import net.sf.basedb.core.SimpleAbsoluteProgressReporter;
    4346import net.sf.basedb.core.StringParameterType;
    4447import net.sf.basedb.core.Job.ExecutionTime;
     
    5255import net.sf.basedb.core.plugin.Request;
    5356import net.sf.basedb.core.plugin.Response;
    54 import net.sf.basedb.util.Enumeration;
     57import net.sf.basedb.core.query.Hql;
     58import net.sf.basedb.core.query.Orders;
     59import net.sf.basedb.util.Values;
    5560import net.sf.basedb.util.zip.FilePacker;
    5661
    5762import java.io.IOException;
     63import java.io.InputStream;
    5864import java.util.ArrayList;
    5965import java.util.Arrays;
    6066import java.util.Collection;
     67import java.util.Collections;
     68import java.util.Date;
    6169import java.util.EnumSet;
    62 import java.util.HashMap;
    6370import java.util.HashSet;
     71import java.util.LinkedList;
    6472import java.util.List;
    65 import java.util.Map;
    6673import java.util.Set;
    6774
    6875/**
    69   Plugin that packs selected files and directories.
    70   Following packing formats are supported:
    71   <ul>
    72     <li>ZIP-file(.zip)
    73     <li>Gzipped TAR-file(.tar.gz)
    74     <li>BZipped-file(tar.bz2)
    75   </ul>
    76   The compressed file can the either be downloaded immediately to a local computer or
     76  Plug-in that packs one or more selected files and directories into a single file.
     77  The actual packing is performed by classes implementing the {@link FilePacker}
     78  interface. To add new formats, create a class that implements the interface and,
     79  register the new class with a {@link net.sf.basedb.core.PluginConfiguration}
     80  item.
     81  <p>
     82  The compressed file can either be downloaded immediately to the local computer or
    7783  stored into BASE's file system.
    7884 
    79   @author Martin
     85  @author Martin, Nicklas
    8086  @version 2.4
    8187  @base.modified $Date: 2007-07-11 15:45:01 +0200 (Mon, 02 Jul 2007) $
     
    8995    (
    9096        "Packed file exporter",
    91         "This plugin is used to packed one or more files/directories" +
    92         "into a selected format.Items to pack can be selected before starting the plugin and " +
    93         "during configuration(or both). The compressed file can be downloaded immediatley" +
    94         "or stored in file system of BASE. The user can choose to have the selected files and " +
     97        "This plugin is used to packed one or more files/directories " +
     98        "into a single file. Items should be selected before starting the plug-in and " +
     99        "they must all be located in the same parent directory. " +
     100        "The compressed file can be downloaded immediately " +
     101        "or stored in the BASE file system. The user can choose to have the selected files and " +
    95102        "directories removed after they've been packed.",
    96103        "2.4",
     
    104111  private static final GuiContext directoryGui = new GuiContext(Item.DIRECTORY, GuiContext.Type.LIST);
    105112  private static final Set<GuiContext> guiContexts = 
    106     new HashSet<GuiContext>(Arrays.asList(fileGui, directoryGui));
    107  
    108   private static final String FORMAT_ZIP = "zip";
    109   private static final String FORMAT_TAR_GZ = "tar.gz";
    110   private static final String FORMAT_BZ = "tar.bz2";
    111   private Map<String, FilePacker> packers = new HashMap<String, FilePacker>(); 
    112  
    113   private static final String COMMAND_CONFIGURE_JOB_STEP2 = "command_configure_job_step2";
     113    Collections.unmodifiableSet(new HashSet<GuiContext>(Arrays.asList(fileGui, directoryGui)));
    114114 
    115115  private static final Set<Permissions> permissions = new HashSet<Permissions>();
     
    117117  // Parameters
    118118  private RequestInformation configureJob;
    119   private RequestInformation configurePacking;
    120   private PluginParameter<String> formatParameter;
     119  private RequestInformation configurePlugin;
    121120  private PluginParameter<Boolean> removeFilesParameter;
    122   private PluginParameter<String> filesParameter;
    123   private PluginParameter<String> directoriesParameter;
    124  
    125   // Counter
    126   private int compressedItems;
    127 
     121  private PluginParameter<String> packerParameter;
     122 
     123  private int numFiles;
     124  private long totalBytes;
     125  private List<Nameable> selectedFilesAndDirs;
     126 
     127  /**
     128    Create new instance.
     129  */
    128130  public PackedFileExporter()
    129   {
    130     packers.put(FORMAT_ZIP, new ZipFilePacker());
    131     packers.put(FORMAT_TAR_GZ, new TarFilePacker(false));
    132     packers.put(FORMAT_BZ, new TarFilePacker(true));
    133   }
    134  
    135   private DbControl dc;
     131  {}
     132 
    136133  /*
    137       @see net.sf.basedb.core.plugin.AbstractExporterPlugin#begin(net.sf.basedb.core.DbControl)
    138   */
    139   protected void begin(DbControl dc)
    140   {
    141     this.dc = dc;
    142     compressedItems = 0;   
    143   }
     134    From the Plugin interface
     135    --------------------------------
     136  */
     137  public About getAbout()
     138  {
     139    return about;
     140  }
     141  public boolean supportsConfigurations()
     142  {
     143    return true;
     144  }
     145  public boolean requiresConfiguration()
     146  {
     147    return true;
     148  }
     149  /**
     150    Request create and delete access to File:s and Directory:s.
     151  */
     152  public Collection<Permissions> getPermissions()
     153  {
     154    if (permissions.size() == 0)
     155    {
     156      permissions.add(new Permissions(Item.FILE, null, EnumSet.of(Permission.CREATE, Permission.DELETE)));
     157      permissions.add(new Permissions(Item.DIRECTORY, null, EnumSet.of(Permission.DELETE)));
     158    }
     159    return permissions;
     160  }
     161  // -------------------------------------------
    144162 
    145163  /*
    146     Find the right packer tool to use and then pack the files and directories.     
    147       @see net.sf.basedb.core.plugin.AbstractExporterPlugin#performExport(net.sf.basedb.core.plugin.ExportOutputStream,
    148         net.sf.basedb.core.ProgressReporter)
    149   */
    150   @Override
    151   @SuppressWarnings("unchecked")
    152   protected void performExport(ExportOutputStream out, ProgressReporter progress)
    153     throws IOException
    154   {
    155     List<String> filePaths = (List<String>)job.getValues("files");       
    156     List<String> directoryPaths = (List<String>)job.getValues("directories");
    157     String compressionFormat = (String)job.getValue("fileFormat");
    158     boolean removeItems = (Boolean)job.getValue("removeItems");
    159     FilePacker packer = packers.get(compressionFormat);
    160        
    161     if (packer == null)
    162     {
    163       throw new BaseException("Packing tool could not be found");
    164     }
    165     // Progressreporter showing how many of the seleced items have been processed.
    166     AbsoluteProgressReporter absoluteProgress = progress == null ?
    167         null : new SimpleAbsoluteProgressReporter(progress, (filePaths.size() + directoryPaths.size()));
    168     packer.setRemoveAfter(removeItems);
    169     out.setMimeType(packer.getMimeType());
    170     compressedItems = packer.pack(dc, out, filePaths, directoryPaths, absoluteProgress);
    171   }
    172  
    173   /*
    174       @see net.sf.basedb.core.plugin.AbstractExporterPlugin#end(boolean)
    175   */
    176   protected void end(boolean success)
    177   {
    178     this.dc = null;
    179   }
    180  
    181   /*
    182       @see net.sf.basedb.core.plugin.AbstractExporterPlugin#getSuccessMessage()
    183   */
    184   protected String getSuccessMessage()
    185   {
    186     return compressedItems + " of the selected directories/files were compressed successfully";
    187   }
    188 
    189   /*
    190       @see net.sf.basedb.core.plugin.InteractivePlugin#configure(net.sf.basedb.core.plugin.GuiContext,
    191         net.sf.basedb.core.plugin.Request, net.sf.basedb.core.plugin.Response)
    192   */
     164    From the InteractivePlugin interface
     165    -------------------------------------------
     166  */
     167  public Set<GuiContext> getGuiContexts()
     168  {
     169    return guiContexts;
     170  }
     171  /**
     172    Check that at least one file or directory has been selected.
     173  */
     174  public String isInContext(GuiContext context, Object item)
     175  {
     176    String message = null;
     177    ItemContext file = sc.getCurrentContext(Item.FILE);
     178    ItemContext directory = sc.getCurrentContext(Item.DIRECTORY);
     179    if (file.getSelected().isEmpty() && directory.getSelected().isEmpty())
     180    {
     181      message = "You must select at least one file or directory.";
     182    }
     183    return message;
     184  }
     185  public RequestInformation getRequestInformation(GuiContext context, String command)
     186    throws BaseException
     187  {
     188    RequestInformation requestInformation = null;
     189    if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
     190    {
     191      requestInformation = getConfigurePluginParameters();
     192    }
     193    else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
     194    {
     195      requestInformation = getConfigureJobParameters(context, null);
     196    }
     197    return requestInformation;
     198  }
    193199  @SuppressWarnings("unchecked")
    194200  public void configure(GuiContext context, Request request, Response response)
     
    197203    try
    198204    {
    199       if (command.equals(Request.COMMAND_CONFIGURE_JOB))
    200       {
    201         RequestInformation ri = getConfigureJobParameters();
     205      if (command.equals(Request.COMMAND_CONFIGURE_PLUGIN))
     206      {
     207        RequestInformation ri = getConfigurePluginParameters();
    202208        List<Throwable> errors = validateRequestParameters(ri.getParameters(), request);
    203209        if (errors != null)
     
    205211          response.setError(errors.size() + " invalid parameters were found in the request", errors);
    206212          return;
    207         }
    208         if (request.getParameterValues("files").isEmpty() && request.getParameterValues("directories").isEmpty())
    209         {
    210           response.setError("Select at least one file or directory to pack", null);
     213        }       
     214       
     215        // Verify that class implements FilePacker
     216        String packerClass = (String)request.getParameterValue("packer");
     217        FilePacker packer = null;
     218        try
     219        {
     220          packer = getPacker(packerClass);
     221        }
     222        catch (Throwable t)
     223        {
     224          response.setError("Class '" + packerClass + "' can't be used for packing", Arrays.asList(t));
    211225          return;
    212226        }
    213 
    214         // Store first part of the parameter values for the job
    215         storeValue(job, request, ri.getParameter("fileFormat"));
    216         storeValues(job, request, ri.getParameter("files"));
    217         storeValues(job, request, ri.getParameter("directories"));
    218         response.setContinue(COMMAND_CONFIGURE_JOB_STEP2);
    219       }
    220       else if (command.equals(COMMAND_CONFIGURE_JOB_STEP2))
    221       {
    222         RequestInformation ri = getConfigurePackingParameters(!request.isAllowedImmediateExecution());
     227        storeValue(configuration, request, ri.getParameter("packer"));
     228       
     229        // Done
     230        response.setDone("Configuration complete for packer " + packerClass +
     231            " (" + packer.getDescription() + ")");
     232      }
     233     
     234      else if (command.equals(Request.COMMAND_CONFIGURE_JOB))
     235      {
     236        RequestInformation ri = getConfigureJobParameters(context, !request.isAllowedImmediateExecution());
    223237        List<Throwable> errors = validateRequestParameters(ri.getParameters(), request);
    224238        if (errors != null)
     
    227241          return;
    228242        }
    229        
    230         String fileExtension = packers.get((String)job.getValue(formatParameter.getName())).getFileExtension();         
    231         if (request.getParameterValue(SAVE_AS) == null)
    232         {
     243 
     244        FilePacker packer = getPacker();
     245        String extension = packer.getFileExtension();
     246       
     247        // Verify SAVE_AS
     248        String saveAs = (String)request.getParameterValue(SAVE_AS);
     249        if (saveAs == null)
     250        {
     251          // No filename is given, check if immediate execution is allowed
    233252          if (!request.isAllowedImmediateExecution())
    234253          {
     
    236255            return;
    237256          }
    238           response.setDownloadImmediately("Pack selected files and directories into compressedFile" + fileExtension, ExecutionTime.SHORTEST, true);
    239257        }
    240258        else
    241259        {
    242           //The finished archive file cannot be saved inside one of the selected directories.
    243           if (isFileInDirectories((String)request.getParameterValue(SAVE_AS), (List<String>)job.getValues("directories")))
     260          if (!saveAs.endsWith(extension)) saveAs += extension;
     261          Boolean overwrite = (Boolean)request.getParameterValue(OVERWRITE);
     262          if (!pathCanBeUsed(saveAs, overwrite))
    244263          {
    245             response.setError("The compressed file cannot be saved inside a directory that are going to be archived", null);
     264            response.setError("File exists: " + saveAs, null);
    246265            return;
    247266          }
    248           if (!pathCanBeUsed((String)request.getParameterValue(ri.getParameter(SAVE_AS).getName()),
    249               (Boolean)request.getParameterValue(ri.getParameter(OVERWRITE).getName())))
     267        }
     268       
     269        // Verify that all selected files and directories are children to the root directory
     270        Directory root = (Directory)request.getParameterValue("root");
     271        for (Nameable item : selectedFilesAndDirs)
     272        {
     273          Directory dir = null;
     274          if (item.getType() == Item.FILE)
    250275          {
    251             response.setError("File: " + (String)request.getParameterValue(ri.getParameter(SAVE_AS).getName()) +
    252                 " already exists in specified format", null);
    253             return;
    254           }
    255           // The finished file needs to have right file extension otherwise it won't be
    256           //readable for other packing tools.
    257           if (!((String)request.getParameterValue(SAVE_AS)).endsWith(fileExtension))
    258           {
    259             job.setValue(SAVE_AS,
    260                 (ParameterType<String>)ri.getParameter(SAVE_AS).getParameterType(),
    261                 ((String)request.getParameterValue(SAVE_AS) + "." + fileExtension));
     276            dir = ((File)item).getDirectory();
    262277          }
    263278          else
    264279          {
    265             storeValue(job, request, ri.getParameter(SAVE_AS));
     280            dir = ((Directory)item).getParent();
    266281          }
     282          if (!root.equals(dir))
     283          {
     284            response.setError(item.getType().toString() + " is not located in root directory: " + root.getPath().toString(), null);
     285            return;
     286          }
     287        }
     288       
     289        // Store the selected files and directories   
     290        storeValue(job, request, ri.getParameter("root"));
     291        ParameterType<Integer> filesAndDirectories = new IntegerParameterType(null, null, null, true, 0, 0, 0, null);
     292        Set<Integer> selectedFiles = sc.getCurrentContext(Item.FILE).getSelected();
     293        if (selectedFiles.size() > 0)
     294        {
     295          job.setValues("files", filesAndDirectories, new ArrayList<Integer>(selectedFiles));
     296        }
     297        Set<Integer> selectedDirectories = sc.getCurrentContext(Item.DIRECTORY).getSelected();
     298        if (selectedDirectories.size() > 0)
     299        {
     300          job.setValues("directories", filesAndDirectories, new ArrayList<Integer>(selectedDirectories));
     301        }
     302       
     303        // Other options
     304        storeValue(job, request, ri.getParameter("removeItems"));
     305        if (saveAs != null)
     306        {
    267307          storeValue(job, request, ri.getParameter(OVERWRITE));
    268           storeValue(job, request, ri.getParameter("removeItems"));         
     308          storeValue(job, (PluginParameter<String>)ri.getParameter(SAVE_AS), saveAs);
    269309          response.setDone("Job configuration is done", ExecutionTime.SHORT);
    270310        }
     311        else
     312        {
     313          response.setDownloadImmediately("Pack selected files and directories", ExecutionTime.SHORTEST, true);
     314        }
     315       
    271316      }
    272317    }
     
    276321    }
    277322  }
    278 
     323  // -------------------------------------------
     324 
    279325  /*
    280       @see net.sf.basedb.core.plugin.InteractivePlugin#getGuiContexts()
    281   */
    282   public Set<GuiContext> getGuiContexts()
    283   {
    284     return guiContexts;
    285   }
    286 
     326    From the AbstractExporterPlugin class
     327    --------------------------------------------
     328  */
     329   
     330  private DbControl dc;
    287331  /*
    288     @see net.sf.basedb.core.plugin.InteractivePlugin#getRequestInformation(net.sf.basedb.core.plugin.GuiContext, java.lang.String)
    289   */
    290   public RequestInformation getRequestInformation(GuiContext context,
    291       String command) throws BaseException
    292   {
    293     RequestInformation requestInformation = null;
    294     if (command.equals(Request.COMMAND_CONFIGURE_JOB))
    295     {
    296       requestInformation = getConfigureJobParameters();
    297     }
    298     else if (command.equals(COMMAND_CONFIGURE_JOB_STEP2))
    299     {
    300       requestInformation = getConfigurePackingParameters(null);
    301     }
    302     return requestInformation;
    303   }
    304 
    305   /*
    306       @see net.sf.basedb.core.plugin.InteractivePlugin#isInContext(net.sf.basedb.core.plugin.GuiContext, java.lang.Object)
    307   */
    308   public String isInContext(GuiContext context, Object item)
    309   {
    310     return null;
    311   }
    312 
    313   /*   
    314       @see net.sf.basedb.core.plugin.Plugin#getAbout()
    315    */
    316   public About getAbout()
    317   {
    318     return about;
    319   }
    320  
    321   /*
    322       @see net.sf.basedb.core.plugin.AbstractPlugin#supportsConfigurations()
    323    */
    324   public boolean supportsConfigurations()
    325   {
    326     return false;
    327   }
    328  
    329   /*
    330       @see net.sf.basedb.core.plugin.AbstractPlugin#requiresConfiguration()
    331    */
    332   public boolean requiresConfiguration()
    333   {
    334     return false;
    335   }
    336  
    337   /*
    338       @see net.sf.basedb.core.plugin.AbstractPlugin#getPermissions()
    339   */
    340   public Collection<Permissions> getPermissions()
    341   {
    342     if (permissions.size() == 0)
    343     {
    344       permissions.add(new Permissions(Item.FILE, null, EnumSet.of(Permission.CREATE, Permission.WRITE)));
    345       permissions.add(new Permissions(Item.DIRECTORY, null, EnumSet.of(Permission.USE)));
    346     }
    347     return permissions;
    348   }
    349  
    350   /**
    351     Configures the first part of the job parameters for this plugin   
    352       @return {@link #configureJob}
    353    */
    354   private RequestInformation getConfigureJobParameters()
     332      @see net.sf.basedb.core.plugin.AbstractExporterPlugin#begin(net.sf.basedb.core.DbControl)
     333  */
     334  @Override
     335  protected void begin(DbControl dc)
     336  {
     337    this.dc = dc;
     338  }
     339 
     340  @Override
     341  @SuppressWarnings("unchecked")
     342  protected void performExport(ExportOutputStream out, ProgressReporter progress)
     343    throws IOException
     344  {
     345    // Get parameters
     346    Directory rootDir = (Directory)job.getValue("root");
     347    List<Integer> files = (List<Integer>)job.getValues("files");       
     348    List<Integer> directories = (List<Integer>)job.getValues("directories");
     349    boolean removeItems = Boolean.TRUE.equals(job.getValue("removeItems"));
     350    FilePacker packer = getPacker();
     351
     352    if (progress != null)
     353    {
     354      progress.display(0, "Checking selected files and directories...");
     355    }
     356   
     357    // Preload files
     358    List<Nameable> itemsToPack = new LinkedList<Nameable>();
     359    loadFilesAndDirectories(dc, itemsToPack, files, directories, true);
     360    numFiles = itemsToPack.size();
     361    int currentFile = 0;
     362    long completedBytes = 0;
     363    float progressFactor = totalBytes == 0 ? 0 : 100.0f / totalBytes;
     364   
     365    // Prepare for compression
     366    String rootPath = rootDir.getPath().toString();
     367    out.setMimeType(packer.getMimeType());
     368    out.setFilename(generateDefaultName(packer, rootDir, itemsToPack));
     369    packer.setOutputStream(out);
     370   
     371    // Compress the files and directories
     372    for (Nameable item : itemsToPack)
     373    {
     374      currentFile++;
     375      String entryPath = null;
     376      Date lastModified = null;
     377      long size = 0;
     378      InputStream in = null;
     379      if (item.getType() == Item.FILE)
     380      {
     381        File f = (File)item;
     382        entryPath = f.getPath().toString();
     383        lastModified = f.getLastUpdate();
     384        in = f.getDownloadStream(0);
     385        if (f.getLocation() == Location.PRIMARY) size = f.getSize();
     386        if (removeItems && f.hasPermission(Permission.DELETE)) f.setRemoved(true);
     387      }
     388      else
     389      {
     390        Directory d = (Directory)item;
     391        entryPath = d.getPath().toString();
     392        if (removeItems && d.hasPermission(Permission.DELETE)) d.setRemoved(true);
     393      }
     394     
     395      // Remove rootPath from entryPath
     396      entryPath = entryPath.substring(rootPath.length()+1);
     397     
     398      // Update progress
     399      if (progress != null)
     400      {
     401        int percent = (int)(completedBytes * progressFactor);
     402        progress.display(percent, "Compressing '" + entryPath + "'; " +
     403          currentFile + " of " + numFiles + " file(s) (" +
     404          Values.formatBytes(completedBytes) + " of " + Values.formatBytes(totalBytes) + ")");
     405      }
     406
     407      // Send to FilePacker
     408      packer.pack(entryPath, in, size, lastModified == null ? 0 : lastModified.getTime());
     409      if (in != null) in.close();
     410      completedBytes += size;
     411    }
     412   
     413    packer.close();
     414    if (progress != null)
     415    {
     416      progress.display(100, "Done. " + numFiles + " file(s) and directories compressed successfully");
     417    }
     418  }
     419  protected void end(boolean success)
     420  {
     421    this.dc = null;
     422  }
     423 
     424  protected String getSuccessMessage()
     425  {
     426    return numFiles + " file(s) and directories compressed successfully.";
     427  }
     428  // -------------------------------------------
     429
     430
     431  private RequestInformation getConfigureJobParameters(GuiContext context, Boolean requireFile)
    355432  {
    356433    if (configureJob == null)
     
    361438        dc = sc.newDbControl();
    362439        List<PluginParameter<?>> parameters = new ArrayList<PluginParameter<?>>();
    363              
    364         // Format parameter   
    365         Enumeration<String, String> formats = new Enumeration<String, String>();
    366         formats.add(FORMAT_ZIP, "Zip-archive (.zip)");
    367         formats.add(FORMAT_TAR_GZ, "GZipped Tar-archive (.tar.gz)");
    368         formats.add(FORMAT_BZ, "BZipped Tar-archive (.tar.bz)");
    369         formatParameter = new PluginParameter<String>
     440
     441        // Get info about current directory and selected files and directories
     442        FilePacker packer = getPacker();
     443        ItemContext fileContext = sc.getCurrentContext(Item.FILE);
     444        ItemContext dirContext = sc.getCurrentContext(Item.DIRECTORY);
     445        Directory currentDir = Directory.getById(dc, dirContext.getId());
     446        selectedFilesAndDirs = new LinkedList<Nameable>();
     447        loadFilesAndDirectories(dc, selectedFilesAndDirs, fileContext.getSelected(), dirContext.getSelected(), false);
     448       
     449        // Save as and overwrite parameter
     450        if (requireFile == null)
     451        {
     452          PluginDefinition pd = job.getPluginDefinition();
     453          requireFile = !pd.getAllowImmediateExecution();
     454        }
     455        String defaultPath = generateDefaultPath(packer, currentDir, selectedFilesAndDirs);
     456       
     457        // Root directory
     458        parameters.add(new PluginParameter<Directory>(
     459          "root",
     460          "Root directory",
     461          "The root directory which must be the parent of all selected files and directories",
     462          new ItemParameterType<Directory>(Directory.class, null, true, 1, null))
     463        );
     464       
     465        // Save as and overwrite
     466        parameters.add(getSaveAsParameter(null, null, defaultPath, requireFile));
     467        parameters.add(getOverwriteParameter(null, null));
     468
     469        // Remove parameter
     470        removeFilesParameter = new PluginParameter<Boolean>
    370471          (
    371             "fileFormat",
    372             "File format",
    373             "Please select which packing format.",
    374             new StringParameterType(255, null, true, 1, 0, 0, formats)
     472            "removeItems",
     473            "Remove files/directories",
     474            "Select if selected files and directories should be flagged for " +
     475            "deletion or not, after they've been packed",
     476            new BooleanParameterType(null, false)
    375477          );
    376         parameters.add(formatParameter);
    377  
    378         // The files and directories that should be packed               
    379         filesParameter = new PluginParameter<String>
    380           (
    381             "files",
    382             "Files",
    383             "Select the files that should be put in the compressed file.",
    384             new PathParameterType(Path.Type.FILE, null, false, 0, 0, 0, null)
    385           );
    386        
    387         directoriesParameter = new PluginParameter<String>
    388           (
    389             "directories",
    390             "Directories",
    391             "Select the directories that should be put in the compressed file.",
    392             new PathParameterType(Path.Type.DIRECTORY, null, false, 0, 0, 0, null)
    393           );
    394         Set<Integer> selectedFileIds = sc.getCurrentContext(Item.FILE).getSelected();
    395         Set<Integer> selectedDirectoryIds = sc.getCurrentContext(Item.DIRECTORY).getSelected();
    396         List<String> filePaths = new ArrayList<String>();
    397         List<String> directoryPaths = new ArrayList<String>();
    398         if (selectedFileIds.size() > 0)
    399         {
    400           for (Integer fileId : selectedFileIds)
    401           {
    402             filePaths.add(File.getById(dc, fileId).getPath().toString());
    403           }
    404         }
    405         if (selectedDirectoryIds.size() > 0)
    406         {
    407           for (Integer directoryId : selectedDirectoryIds)
    408           {
    409             directoryPaths.add(Directory.getById(dc, directoryId).getPath().toString());
    410           }
    411         }
    412         parameters.add(filesParameter);
    413         parameters.add(directoriesParameter);
    414         job.setValues("files", new PathParameterType(Path.Type.FILE, null, false, 0, 0, 0, null), filePaths);
    415         job.setValues("directories", new PathParameterType(Path.Type.DIRECTORY, null, false, 0, 0, 0, null), directoryPaths);
     478        parameters.add(removeFilesParameter);
    416479       
    417480        configureJob = new RequestInformation
    418481          (
    419482            Request.COMMAND_CONFIGURE_JOB,
    420             "Files, directories and format",
    421             "Set which files and diretories that should be compressed" +
    422             " and the compression format to use.",
     483            "Select compression format",
     484            "Select the compression format to use and a few other options.",
    423485            parameters
    424486          );
     
    435497  }
    436498 
    437   /**
    438     Configures the second part of the job parameters for this plugin
    439       @param requireFile True if this plugin cannot download immediately, FALSE otherwise
    440       @return RequestInformation with configured parameters
    441   */
    442   private RequestInformation getConfigurePackingParameters(Boolean requireFile)
    443   {
    444     if (configurePacking == null)
     499  private RequestInformation getConfigurePluginParameters()
     500  {
     501    if (configurePlugin == null)
    445502    {
    446503      List<PluginParameter<?>> parameters = new ArrayList<PluginParameter<?>>();
    447 
    448       // Remove parameter
    449       removeFilesParameter = new PluginParameter<Boolean>
    450         (
    451           "removeItems",
    452           "Remove files/directories",
    453           "Define if selected files and directories should be flagged for " +
    454           "deletion or not, after they've been packed",
    455           new BooleanParameterType(false, true)
    456         );
    457       parameters.add(removeFilesParameter);
     504      packerParameter = new PluginParameter<String>(
     505        "packer",
     506        "Packer class",
     507        "Enter the name of the class that is responsible for packing the files. It " +
     508        "must be a class that implements the FilePacker interface.",
     509        new StringParameterType(255, null, true)
     510      );
     511      parameters.add(packerParameter);
    458512     
    459       String fileExtension = packers.get((String)job.getValue(formatParameter.getName())).getFileExtension();
    460      
    461       // Save as and overwrite parameter
    462       if (requireFile == null)
    463       {
    464         PluginDefinition pd = job.getPluginDefinition();
    465         requireFile = !pd.getAllowImmediateExecution();
    466       }
    467       String defaultPath = "~/compressedFile." + fileExtension;     
    468       parameters.add(getSaveAsParameter(null, null, defaultPath, requireFile));
    469       parameters.add(getOverwriteParameter(null, null));
    470 
    471       configurePacking = new RequestInformation
     513      configurePlugin = new RequestInformation
    472514      (
    473         COMMAND_CONFIGURE_JOB_STEP2,
    474         "Packing options",
    475         "Set saving options for the compressed file",
     515        Request.COMMAND_CONFIGURE_PLUGIN,
     516        "Select packer class",
     517        "Enter the name of the FilePacker implementation.",
    476518        parameters
    477519      );
    478520    }
    479     return configurePacking;
    480   }
    481  
    482   /**
    483     Checks if filepath is inside any of listed directories
    484       @param filePath the file path to check
    485       @param directories a list with directories.
    486       @return FALSE if the file isn't inside any of the directories, TRUE otherwise.
    487    */
    488   private boolean isFileInDirectories(String filePath, List<String> directories)
    489   {
    490     boolean isInDirectory = false;
    491     String fileDir = filePath.substring(0, filePath.lastIndexOf("/"));
    492     if ( (directories != null) && (directories.size() > 0) && (fileDir.length() > 0))
    493     {           
    494       for (String dir : directories)
    495       {
    496         if (fileDir.contains(dir))
    497         {
    498           isInDirectory = true;
    499         }   
    500       }
    501     }
    502     return isInDirectory;
    503   }
     521    return configurePlugin;
     522  }
     523 
     524
     525  /**
     526    Load specified files and directories and put them in <code>loadedFiles</code>.
     527    This method also calculates the total size of all files and stores it in
     528    {@link #totalBytes}.
     529
     530    @param dc The DbControl to use for loading
     531    @param loadedFiles The files and directories will be added to this collection
     532    @param files The ID of the files to load
     533    @param directories The ID of the directories to load
     534    @param recursive If we should recurse into subdirectories or not
     535  */
     536  private void loadFilesAndDirectories(DbControl dc, Collection<Nameable> loadedFiles, Collection<Integer> files, Collection<Integer> directories, boolean recursive)
     537  {
     538    totalBytes = 0;
     539    // Load selected files
     540    if (files != null)
     541    {
     542      for (Integer fileId : files)
     543      {
     544        try
     545        {
     546          File f = File.getById(dc, fileId);
     547          loadedFiles.add(f);
     548          if (f.getLocation() == Location.PRIMARY) totalBytes += f.getSize();
     549        }
     550        catch (Throwable t)
     551        {}
     552      }
     553    }
     554    // Load selected directories and files if recursive
     555    if (directories != null)
     556    {
     557      for (Integer dirId : directories)
     558      {
     559        try
     560        {
     561          Directory dir = Directory.getById(dc, dirId);
     562          loadedFiles.add(dir);
     563          if (recursive) loadFiles(dc, loadedFiles, dir);
     564        }
     565        catch (Throwable t)
     566        {}
     567      }
     568    }
     569  }
     570 
     571  /**
     572    Load files and subdirectories in the given directory.
     573    @param dc The DbControl to use for loading
     574    @param loadedFiles The files and directories will be added to this collection
     575    @param directory The parent directory
     576  */
     577  private void loadFiles(DbControl dc, Collection<Nameable> loadedFiles, Directory directory)
     578  {
     579    ItemQuery<File> fileQuery = directory.getFiles();
     580    fileQuery.include(Include.MINE, Include.OTHERS, Include.SHARED, Include.IN_PROJECT);
     581    fileQuery.order(Orders.asc(Hql.property("name")));
     582   
     583    for (File f : fileQuery.list(dc))
     584    {
     585      loadedFiles.add(f);
     586      if (f.getLocation() == Location.PRIMARY) totalBytes += f.getSize();
     587    }
     588   
     589    ItemQuery<Directory> dirQuery = directory.getSubDirectories();
     590    dirQuery.include(Include.MINE, Include.OTHERS, Include.SHARED, Include.IN_PROJECT);
     591    dirQuery.order(Orders.asc(Hql.property("name")));
     592   
     593    for (Directory dir : dirQuery.list(dc))
     594    {
     595      loadedFiles.add(dir);
     596      loadFiles(dc, loadedFiles, dir);
     597    }
     598  }
     599 
     600  /**
     601    Get the FilePacker that has been configured in the current configuration.
     602  */
     603  private FilePacker getPacker()
     604  {
     605    String packerClass = (String)configuration.getValue("packer");
     606    FilePacker packer = null;
     607    try
     608    {
     609      packer = getPacker(packerClass);
     610    }
     611    catch (Throwable t)
     612    {
     613      throw new BaseException("Class '" + packerClass + "' can't be used for packing", t);
     614    }
     615    return packer;
     616  }
     617 
     618  /**
     619    Create a FilePacker from the given class.
     620    @param packerClass The class name of a class that implements FilePacker
     621  */
     622  private FilePacker getPacker(String packerClass)
     623    throws Exception
     624  {
     625    return (FilePacker)Class.forName(packerClass).newInstance();
     626  }
     627 
     628  /**
     629    Generate a default path for the packed file. If the selection contains only one
     630    file or directory, the name of that item is used, otherwise the name of the root
     631    directory is used. The file extension from  {@link FilePacker#getFileExtension()} is
     632    automatically appeneded.
     633   
     634    @param packer The current FilePacker
     635    @param rootDir The root directory
     636    @param selectedItems The selected items (not including subdirectories)
     637  */
     638  private String generateDefaultPath(FilePacker packer, Directory rootDir, Collection<Nameable> selectedItems)
     639  {
     640    String defaultPath = rootDir.getPath().toString() + "/" + generateDefaultName(packer, rootDir, selectedItems);
     641    return defaultPath;
     642  }
     643
     644  /**
     645    Generate a default name without a path for the packed file.
     646    @see #generateDefaultPath(FilePacker, Directory, Collection)
     647  */
     648  private String generateDefaultName(FilePacker packer, Directory currentDir, Collection<Nameable> selectedItems)
     649  {
     650    String defaultName = "";
     651    if (selectedItems.size() == 1)
     652    {
     653      defaultName += selectedItems.iterator().next().getName();
     654    }
     655    else
     656    {
     657      defaultName += currentDir.getName();
     658    }
     659    defaultName += "."+packer.getFileExtension();
     660    return defaultName;
     661  }
     662 
    504663}
Note: See TracChangeset for help on using the changeset viewer.