src/core/net/sf/basedb/util/FileUtil.java

Code
Comments
Other
Rev Date Author Line
29 22 Feb 05 nicklas 1 /*
192 17 Mar 05 jari 2   $Id$
29 22 Feb 05 nicklas 3
4889 06 Apr 09 nicklas 4   Copyright (C) 2005, 2006 Jari Häkkinen, Nicklas Nordborg
3675 16 Aug 07 jari 5   Copyright (C) 2007 Nicklas Nordborg
29 22 Feb 05 nicklas 6
2304 22 May 06 jari 7   This file is part of BASE - BioArray Software Environment.
2304 22 May 06 jari 8   Available at http://base.thep.lu.se/
29 22 Feb 05 nicklas 9
29 22 Feb 05 nicklas 10   BASE is free software; you can redistribute it and/or
29 22 Feb 05 nicklas 11   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 12   as published by the Free Software Foundation; either version 3
29 22 Feb 05 nicklas 13   of the License, or (at your option) any later version.
29 22 Feb 05 nicklas 14
29 22 Feb 05 nicklas 15   BASE is distributed in the hope that it will be useful,
29 22 Feb 05 nicklas 16   but WITHOUT ANY WARRANTY; without even the implied warranty of
29 22 Feb 05 nicklas 17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29 22 Feb 05 nicklas 18   GNU General Public License for more details.
29 22 Feb 05 nicklas 19
29 22 Feb 05 nicklas 20   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 21   along with BASE. If not, see <http://www.gnu.org/licenses/>.
29 22 Feb 05 nicklas 22 */
29 22 Feb 05 nicklas 23 package net.sf.basedb.util;
29 22 Feb 05 nicklas 24
3719 12 Sep 07 nicklas 25 import net.sf.basedb.core.AbsoluteProgressReporter;
3700 23 Aug 07 martin 26 import net.sf.basedb.core.BaseException;
647 25 May 05 nicklas 27 import net.sf.basedb.core.DbControl;
647 25 May 05 nicklas 28 import net.sf.basedb.core.Directory;
3700 23 Aug 07 martin 29 import net.sf.basedb.core.Include;
3700 23 Aug 07 martin 30 import net.sf.basedb.core.InvalidDataException;
1418 07 Oct 05 nicklas 31 import net.sf.basedb.core.ItemQuery;
4794 02 Mar 09 nicklas 32 import net.sf.basedb.core.ItemResultIterator;
4794 02 Mar 09 nicklas 33 import net.sf.basedb.core.PermissionDeniedException;
5374 03 Aug 10 nicklas 34 import net.sf.basedb.core.ProgressReporter;
5374 03 Aug 10 nicklas 35 import net.sf.basedb.core.SimpleAbsoluteProgressReporter;
1979 14 Feb 06 nicklas 36 import net.sf.basedb.core.Type;
647 25 May 05 nicklas 37 import net.sf.basedb.core.query.Expressions;
1418 07 Oct 05 nicklas 38 import net.sf.basedb.core.query.Hql;
662 27 May 05 nicklas 39 import net.sf.basedb.core.query.Orders;
3700 23 Aug 07 martin 40 import net.sf.basedb.core.query.Restrictions;
4120 04 Feb 08 nicklas 41 import net.sf.basedb.core.signal.ThreadSignalHandler;
5010 23 Jun 09 nicklas 42 import net.sf.basedb.util.filter.Filter;
647 25 May 05 nicklas 43
3700 23 Aug 07 martin 44 import java.io.BufferedInputStream;
2038 21 Feb 06 nicklas 45 import java.io.BufferedOutputStream;
6880 21 Apr 15 nicklas 46 import java.io.Closeable;
2038 21 Feb 06 nicklas 47 import java.io.File;
3832 15 Oct 07 nicklas 48 import java.io.FileFilter;
3700 23 Aug 07 martin 49 import java.io.FileInputStream;
2038 21 Feb 06 nicklas 50 import java.io.FileOutputStream;
29 22 Feb 05 nicklas 51 import java.io.IOException;
29 22 Feb 05 nicklas 52 import java.io.InputStream;
29 22 Feb 05 nicklas 53 import java.io.OutputStream;
3510 19 Jun 07 nicklas 54 import java.io.PushbackInputStream;
4867 31 Mar 09 nicklas 55 import java.io.Reader;
4867 31 Mar 09 nicklas 56 import java.io.Writer;
5374 03 Aug 10 nicklas 57 import java.util.ArrayList;
3700 23 Aug 07 martin 58 import java.util.Arrays;
7310 20 Mar 17 nicklas 59 import java.util.Collection;
5374 03 Aug 10 nicklas 60 import java.util.Collections;
3700 23 Aug 07 martin 61 import java.util.HashMap;
4794 02 Mar 09 nicklas 62 import java.util.HashSet;
3700 23 Aug 07 martin 63 import java.util.LinkedList;
3700 23 Aug 07 martin 64 import java.util.List;
3700 23 Aug 07 martin 65 import java.util.Map;
4794 02 Mar 09 nicklas 66 import java.util.Set;
29 22 Feb 05 nicklas 67
29 22 Feb 05 nicklas 68 /**
29 22 Feb 05 nicklas 69   This class collects some useful methods for file and stream
29 22 Feb 05 nicklas 70   handling.
29 22 Feb 05 nicklas 71
29 22 Feb 05 nicklas 72   @author Nicklas
29 22 Feb 05 nicklas 73   @version 2.0
1979 14 Feb 06 nicklas 74   @base.modified $Date$
29 22 Feb 05 nicklas 75 */
29 22 Feb 05 nicklas 76 public class FileUtil
29 22 Feb 05 nicklas 77 {
29 22 Feb 05 nicklas 78   private static final int BUFFER_SIZE = 512;
29 22 Feb 05 nicklas 79
29 22 Feb 05 nicklas 80   /**
29 22 Feb 05 nicklas 81     Copy from the input stream to the output stream
29 22 Feb 05 nicklas 82     until end of file is reached.
29 22 Feb 05 nicklas 83     @param in The <code>InputStream</code> to read from
29 22 Feb 05 nicklas 84     @param out The <code>OutputStream</code> to write to
29 22 Feb 05 nicklas 85     @return The number of bytes copied
29 22 Feb 05 nicklas 86     @throws IOException This exception is thrown if there is an error
29 22 Feb 05 nicklas 87   */
29 22 Feb 05 nicklas 88   public static long copy(InputStream in, OutputStream out)
29 22 Feb 05 nicklas 89     throws IOException
29 22 Feb 05 nicklas 90   {
3719 12 Sep 07 nicklas 91     return copy(in, out, null);
3719 12 Sep 07 nicklas 92   }
3719 12 Sep 07 nicklas 93
3719 12 Sep 07 nicklas 94   /**
3719 12 Sep 07 nicklas 95     Copy from the input stream to the output stream
3719 12 Sep 07 nicklas 96     until end of file is reached.
3719 12 Sep 07 nicklas 97     @param in The <code>InputStream</code> to read from
3719 12 Sep 07 nicklas 98     @param out The <code>OutputStream</code> to write to
3719 12 Sep 07 nicklas 99     @param progress An optional progress reporter
3719 12 Sep 07 nicklas 100     @return The number of bytes copied
3719 12 Sep 07 nicklas 101     @throws IOException This exception is thrown if there is an error
3719 12 Sep 07 nicklas 102     @since 2.5
3719 12 Sep 07 nicklas 103   */
3719 12 Sep 07 nicklas 104   public static long copy(InputStream in, OutputStream out, AbsoluteProgressReporter progress)
3719 12 Sep 07 nicklas 105     throws IOException
3719 12 Sep 07 nicklas 106   {
29 22 Feb 05 nicklas 107     int bytes = 0;
58 01 Mar 05 nicklas 108     long totalBytes = 0;
29 22 Feb 05 nicklas 109     byte[] buffer = new byte[BUFFER_SIZE];
3719 12 Sep 07 nicklas 110   
29 22 Feb 05 nicklas 111     while (bytes != -1) // -1 = end of stream
29 22 Feb 05 nicklas 112     {
4120 04 Feb 08 nicklas 113       ThreadSignalHandler.checkInterrupted();
29 22 Feb 05 nicklas 114       bytes = in.read(buffer, 0, buffer.length);
29 22 Feb 05 nicklas 115       if (bytes > 0)
29 22 Feb 05 nicklas 116       {
58 01 Mar 05 nicklas 117         totalBytes += bytes;
29 22 Feb 05 nicklas 118         out.write(buffer, 0, bytes);
3719 12 Sep 07 nicklas 119         if (progress != null) progress.displayAbsolute(totalBytes, null);
29 22 Feb 05 nicklas 120       }
29 22 Feb 05 nicklas 121     }
5384 13 Aug 10 nicklas 122     out.flush();
58 01 Mar 05 nicklas 123     return totalBytes;
29 22 Feb 05 nicklas 124   }
3719 12 Sep 07 nicklas 125
4867 31 Mar 09 nicklas 126   /**
4867 31 Mar 09 nicklas 127     Copy from the reader to the writer until end of file is reached.
4867 31 Mar 09 nicklas 128     @param in The <code>Reader</code> to read from
4867 31 Mar 09 nicklas 129     @param out The <code>Writer</code> to write to
4867 31 Mar 09 nicklas 130     @return The number of characters copied
4867 31 Mar 09 nicklas 131     @throws IOException This exception is thrown if there is an error
4867 31 Mar 09 nicklas 132     @since 2.12
4867 31 Mar 09 nicklas 133   */
4867 31 Mar 09 nicklas 134   public static long copy(Reader in, Writer out)
4867 31 Mar 09 nicklas 135     throws IOException
4867 31 Mar 09 nicklas 136   {
4867 31 Mar 09 nicklas 137     return copy(in, out, null);
4867 31 Mar 09 nicklas 138   }
3606 27 Jul 07 nicklas 139   
3606 27 Jul 07 nicklas 140   /**
4867 31 Mar 09 nicklas 141     Copy from the reader to the writer until end of file is reached.
4867 31 Mar 09 nicklas 142     @param in The <code>Reader</code> to read from
4867 31 Mar 09 nicklas 143     @param out The <code>Writer</code> to write to
4867 31 Mar 09 nicklas 144     @param progress An optional progress reporter
4867 31 Mar 09 nicklas 145     @return The number of characters copied
4867 31 Mar 09 nicklas 146     @throws IOException This exception is thrown if there is an error
4867 31 Mar 09 nicklas 147     @since 2.12
4867 31 Mar 09 nicklas 148   */
4867 31 Mar 09 nicklas 149   public static long copy(Reader in, Writer out, AbsoluteProgressReporter progress)
4867 31 Mar 09 nicklas 150     throws IOException
4867 31 Mar 09 nicklas 151   {
4867 31 Mar 09 nicklas 152     int numRead = 0;
4867 31 Mar 09 nicklas 153     long totalRead = 0;
4867 31 Mar 09 nicklas 154     char[] buffer = new char[BUFFER_SIZE];
4867 31 Mar 09 nicklas 155   
4867 31 Mar 09 nicklas 156     while (numRead != -1) // -1 = end of stream
4867 31 Mar 09 nicklas 157     {
4867 31 Mar 09 nicklas 158       ThreadSignalHandler.checkInterrupted();
4867 31 Mar 09 nicklas 159       numRead = in.read(buffer, 0, buffer.length);
4867 31 Mar 09 nicklas 160       if (numRead > 0)
4867 31 Mar 09 nicklas 161       {
4867 31 Mar 09 nicklas 162         totalRead += numRead;
4867 31 Mar 09 nicklas 163         out.write(buffer, 0, numRead);
4867 31 Mar 09 nicklas 164         if (progress != null) progress.displayAbsolute(totalRead, null);
4867 31 Mar 09 nicklas 165       }
4867 31 Mar 09 nicklas 166     }
4867 31 Mar 09 nicklas 167     return totalRead;
4867 31 Mar 09 nicklas 168   }
4867 31 Mar 09 nicklas 169   
4867 31 Mar 09 nicklas 170   /**
3606 27 Jul 07 nicklas 171     Read from the input stream until the end is reached.
3606 27 Jul 07 nicklas 172     
3606 27 Jul 07 nicklas 173     @param in The <code>InputStream</code> to read from
3606 27 Jul 07 nicklas 174     @return The number of bytes copied
3606 27 Jul 07 nicklas 175     @throws IOException This exception is thrown if there is an error
3606 27 Jul 07 nicklas 176     @since 2.4
3606 27 Jul 07 nicklas 177   */
3606 27 Jul 07 nicklas 178   public static long read(InputStream in)
3606 27 Jul 07 nicklas 179     throws IOException
3606 27 Jul 07 nicklas 180   {
3606 27 Jul 07 nicklas 181     int bytes = 0;
3606 27 Jul 07 nicklas 182     long totalBytes = 0;
3606 27 Jul 07 nicklas 183     byte[] buffer = new byte[BUFFER_SIZE];
3606 27 Jul 07 nicklas 184   
3606 27 Jul 07 nicklas 185     while (bytes != -1) // -1 = end of stream
3606 27 Jul 07 nicklas 186     {
4120 04 Feb 08 nicklas 187       ThreadSignalHandler.checkInterrupted();
3606 27 Jul 07 nicklas 188       bytes = in.read(buffer, 0, buffer.length);
3606 27 Jul 07 nicklas 189       if (bytes > 0)
3606 27 Jul 07 nicklas 190       {
3606 27 Jul 07 nicklas 191         totalBytes += bytes;
3606 27 Jul 07 nicklas 192       }
3606 27 Jul 07 nicklas 193     }
3606 27 Jul 07 nicklas 194     return totalBytes;
3606 27 Jul 07 nicklas 195   }
29 22 Feb 05 nicklas 196
29 22 Feb 05 nicklas 197   /**
4594 21 Oct 08 nicklas 198     Close an input stream without throwing an exception.
4594 21 Oct 08 nicklas 199     @param in The input stream to close
4594 21 Oct 08 nicklas 200     @since 2.9
4594 21 Oct 08 nicklas 201   */
4594 21 Oct 08 nicklas 202   public static void close(InputStream in)
4594 21 Oct 08 nicklas 203   {
4594 21 Oct 08 nicklas 204     if (in == null) return;
4594 21 Oct 08 nicklas 205     try
4594 21 Oct 08 nicklas 206     {
4594 21 Oct 08 nicklas 207       in.close();
4594 21 Oct 08 nicklas 208     }
4594 21 Oct 08 nicklas 209     catch (Throwable t)
4594 21 Oct 08 nicklas 210     {}
4594 21 Oct 08 nicklas 211   }
4594 21 Oct 08 nicklas 212   
4594 21 Oct 08 nicklas 213   /**
4594 21 Oct 08 nicklas 214     Close an output stream without throwing an exception.
4594 21 Oct 08 nicklas 215     @param out The output stream to close
4594 21 Oct 08 nicklas 216     @since 2.9
4594 21 Oct 08 nicklas 217   */
4594 21 Oct 08 nicklas 218   public static void close(OutputStream out)
4594 21 Oct 08 nicklas 219   {
4594 21 Oct 08 nicklas 220     if (out == null) return;
4594 21 Oct 08 nicklas 221     try
4594 21 Oct 08 nicklas 222     {
4594 21 Oct 08 nicklas 223       out.close();
4594 21 Oct 08 nicklas 224     }
4594 21 Oct 08 nicklas 225     catch (Throwable t)
4594 21 Oct 08 nicklas 226     {}
4594 21 Oct 08 nicklas 227   }
4594 21 Oct 08 nicklas 228   
4594 21 Oct 08 nicklas 229   /**
6880 21 Apr 15 nicklas 230     Close a {@link Closeable} without throwing an exception.
6880 21 Apr 15 nicklas 231     @param c The object to close
6880 21 Apr 15 nicklas 232     @since 3.5
6880 21 Apr 15 nicklas 233   */
6880 21 Apr 15 nicklas 234   public static void close(Closeable c)
6880 21 Apr 15 nicklas 235   {
6880 21 Apr 15 nicklas 236     if (c == null) return;
6880 21 Apr 15 nicklas 237     try
6880 21 Apr 15 nicklas 238     {
6880 21 Apr 15 nicklas 239       c.close();
6880 21 Apr 15 nicklas 240     }
6880 21 Apr 15 nicklas 241     catch (Throwable t)
6880 21 Apr 15 nicklas 242     {}
6880 21 Apr 15 nicklas 243   }
6880 21 Apr 15 nicklas 244
7912 18 Feb 21 nicklas 245   /**
7912 18 Feb 21 nicklas 246     Close a {@link AutoCloseable} without throwing an exception.
7912 18 Feb 21 nicklas 247     @param c The object to close
7912 18 Feb 21 nicklas 248     @since 3.18
7912 18 Feb 21 nicklas 249   */
7912 18 Feb 21 nicklas 250   public static void close(AutoCloseable c)
7912 18 Feb 21 nicklas 251   {
7912 18 Feb 21 nicklas 252     if (c == null) return;
7912 18 Feb 21 nicklas 253     try
7912 18 Feb 21 nicklas 254     {
7912 18 Feb 21 nicklas 255       c.close();
7912 18 Feb 21 nicklas 256     }
7912 18 Feb 21 nicklas 257     catch (Throwable t)
7912 18 Feb 21 nicklas 258     {}
7912 18 Feb 21 nicklas 259   }
6880 21 Apr 15 nicklas 260   
6880 21 Apr 15 nicklas 261   /**
2038 21 Feb 06 nicklas 262     Get a buffered <code>InputStream</code> object reading from
29 22 Feb 05 nicklas 263     the specified file.
29 22 Feb 05 nicklas 264
2038 21 Feb 06 nicklas 265     @param file The path to the file to read from
29 22 Feb 05 nicklas 266     @return A buffered <code>InputStream</code>
29 22 Feb 05 nicklas 267     @throws IOException If the stream cannot be opened
29 22 Feb 05 nicklas 268   */
2038 21 Feb 06 nicklas 269   public static InputStream getInputStream(File file)
29 22 Feb 05 nicklas 270     throws IOException
29 22 Feb 05 nicklas 271   {
2038 21 Feb 06 nicklas 272     return new BufferedInputStream(new FileInputStream(file));
29 22 Feb 05 nicklas 273   }
647 25 May 05 nicklas 274   
647 25 May 05 nicklas 275   /**
2038 21 Feb 06 nicklas 276     Get a buffered <code>OutputStream</code> object writing to
2038 21 Feb 06 nicklas 277     the specified file.
2038 21 Feb 06 nicklas 278   
2038 21 Feb 06 nicklas 279     @param file The path to the file to write to
2038 21 Feb 06 nicklas 280     @return A buffered <code>OutputStream</code>
2038 21 Feb 06 nicklas 281     @throws IOException If the stream cannot be opened
2038 21 Feb 06 nicklas 282   */
2038 21 Feb 06 nicklas 283   public static OutputStream getOutputStream(File file)
2038 21 Feb 06 nicklas 284     throws IOException
2038 21 Feb 06 nicklas 285   {
2038 21 Feb 06 nicklas 286     return new BufferedOutputStream(new FileOutputStream(file));
2038 21 Feb 06 nicklas 287   }
2038 21 Feb 06 nicklas 288   
2038 21 Feb 06 nicklas 289   /**
3510 19 Jun 07 nicklas 290     Take a peek at an input stream and check if the first few bytes
3510 19 Jun 07 nicklas 291     matches the bCheck parameter. After the check the bytes are
3510 19 Jun 07 nicklas 292     pushed back to the stream again. This method is useful to check
3510 19 Jun 07 nicklas 293     the file format of a file, which can often be done by checing
3510 19 Jun 07 nicklas 294     the first few bytes at the start of the file. For example
3510 19 Jun 07 nicklas 295     to check if the file is gzip file:
3510 19 Jun 07 nicklas 296     <p>
3510 19 Jun 07 nicklas 297     <code>bCheck = new byte[] { 0x1f, (byte)0x8b };</code>
3510 19 Jun 07 nicklas 298     
3510 19 Jun 07 nicklas 299     @param pin The input stream to read from which must have
3510 19 Jun 07 nicklas 300       large enough buffer to be able to unread the bytes
3510 19 Jun 07 nicklas 301     @param bCheck The byte values to use for comparison
3510 19 Jun 07 nicklas 302     @return TRUE if the file matches the specified bytes, 
3510 19 Jun 07 nicklas 303       FALSE otherwise
3510 19 Jun 07 nicklas 304     @throws IOException If there is an error reading or unreading
3510 19 Jun 07 nicklas 305       the bytes
3510 19 Jun 07 nicklas 306     @throws NullPointerException If pin or bCheck is null
3510 19 Jun 07 nicklas 307     @throws IllegalArgumentException If bCheck is zero length
3510 19 Jun 07 nicklas 308     @since 2.4
3510 19 Jun 07 nicklas 309   */
3510 19 Jun 07 nicklas 310   public static boolean checkMagicNumber(PushbackInputStream pin, byte[] bCheck)
3510 19 Jun 07 nicklas 311     throws IOException
3510 19 Jun 07 nicklas 312   {
3510 19 Jun 07 nicklas 313     if (pin == null) throw new NullPointerException("pin");
3510 19 Jun 07 nicklas 314     if (bCheck == null) throw new NullPointerException("bCheck");
3510 19 Jun 07 nicklas 315     if (bCheck.length == 0) throw new IllegalArgumentException("bCheck has zero length");
3510 19 Jun 07 nicklas 316     byte[] b = new byte[bCheck.length];
5384 13 Aug 10 nicklas 317     int numRead = pin.read(b);
5384 13 Aug 10 nicklas 318     boolean match = numRead == bCheck.length && Arrays.equals(b, bCheck);
5384 13 Aug 10 nicklas 319     if (numRead > 0) pin.unread(b, 0, numRead);
3510 19 Jun 07 nicklas 320     return match;
3510 19 Jun 07 nicklas 321   }
3510 19 Jun 07 nicklas 322   
3510 19 Jun 07 nicklas 323   /**
4034 05 Dec 07 martin 324     Get the complete tree of sub-directories from a given directory.
4794 02 Mar 09 nicklas 325     <p>
4794 02 Mar 09 nicklas 326     <b>NOTE!!! This method has very bad performance on large directory trees.
4794 02 Mar 09 nicklas 327     Consider if it is not possible to use the {@link 
4794 02 Mar 09 nicklas 328     #loadMinimalDirectoryTree(DbControl, Directory...)}
4794 02 Mar 09 nicklas 329     instead together with lazy loading of subdirectories.</b>
4794 02 Mar 09 nicklas 330     
4034 05 Dec 07 martin 331      @param dc DbControl used to access the database. 
647 25 May 05 nicklas 332     @param directory The directory to start with
647 25 May 05 nicklas 333     @return A <code>Map</code> which maps a directory to a 
4034 05 Dec 07 martin 334       list of it's sub-directories. Directories without sub-directories
647 25 May 05 nicklas 335       are not included in the map.
647 25 May 05 nicklas 336     @throws InvalidDataException If the directory is null
647 25 May 05 nicklas 337     @throws BaseException If there is another error
647 25 May 05 nicklas 338   */
647 25 May 05 nicklas 339   public static Map<Directory, List<Directory>> getDirectoryTree(DbControl dc, Directory directory)
647 25 May 05 nicklas 340     throws InvalidDataException, BaseException
647 25 May 05 nicklas 341   {
1418 07 Oct 05 nicklas 342     ItemQuery<Directory> query = Directory.getQuery();
1418 07 Oct 05 nicklas 343     query.restrict(
647 25 May 05 nicklas 344       Restrictions.in(
1418 07 Oct 05 nicklas 345         Hql.property("parent"),
647 25 May 05 nicklas 346         Expressions.parameter("parents")
647 25 May 05 nicklas 347       )
647 25 May 05 nicklas 348     );
1418 07 Oct 05 nicklas 349     query.order(Orders.asc(Hql.property("name")));
2301 22 May 06 nicklas 350     query.include(Include.MINE, Include.SHARED, Include.OTHERS, Include.NOT_REMOVED, Include.REMOVED);
647 25 May 05 nicklas 351     
647 25 May 05 nicklas 352     Map<Directory, List<Directory>> tree = new HashMap<Directory, List<Directory>>();
647 25 May 05 nicklas 353     List<Integer> parents = new LinkedList<Integer>();
647 25 May 05 nicklas 354     parents.add(directory.getId());
647 25 May 05 nicklas 355     while (parents.size() > 0)
647 25 May 05 nicklas 356     {
1979 14 Feb 06 nicklas 357       query.setParameter("parents", parents, Type.INT);
1418 07 Oct 05 nicklas 358       List<Directory> children = query.list(dc);
647 25 May 05 nicklas 359       parents.clear();
647 25 May 05 nicklas 360       for (Directory child : children)
647 25 May 05 nicklas 361       {
647 25 May 05 nicklas 362         Directory parent = child.getParent();
647 25 May 05 nicklas 363         if (!tree.containsKey(parent))
647 25 May 05 nicklas 364         {
647 25 May 05 nicklas 365           tree.put(parent, new LinkedList<Directory>());
647 25 May 05 nicklas 366         }
647 25 May 05 nicklas 367         tree.get(parent).add(child);
647 25 May 05 nicklas 368         parents.add(child.getId());
647 25 May 05 nicklas 369       }
647 25 May 05 nicklas 370     }
647 25 May 05 nicklas 371     return tree;
647 25 May 05 nicklas 372   }
3698 21 Aug 07 martin 373   /**
4794 02 Mar 09 nicklas 374     Load a minimal directory tree that includes at least all the specified directories 
4794 02 Mar 09 nicklas 375     and their parent directories all the way up to the root or as far as the logged in user
4794 02 Mar 09 nicklas 376     has read permission.
4794 02 Mar 09 nicklas 377     @param dc A DbControl for accessing the database
4794 02 Mar 09 nicklas 378     @param directories An array of directories
4794 02 Mar 09 nicklas 379     @return A <code>Map</code> which maps a directory to a 
4794 02 Mar 09 nicklas 380       list of it's sub-directories. The map is guaranteed to contain an entry for each of
4794 02 Mar 09 nicklas 381       the given directories, but the list may be empty if there are no subdirectories
4794 02 Mar 09 nicklas 382     @throws BaseException If there is an error
4794 02 Mar 09 nicklas 383     @since 2.11
4794 02 Mar 09 nicklas 384   */
4794 02 Mar 09 nicklas 385   public static Map<Directory, List<Directory>> loadMinimalDirectoryTree(DbControl dc, Directory... directories)
4794 02 Mar 09 nicklas 386     throws InvalidDataException, BaseException
4794 02 Mar 09 nicklas 387   {
7310 20 Mar 17 nicklas 388     return loadMinimalDirectoryTree(dc, Include.ALL, directories);
7310 20 Mar 17 nicklas 389   }
7310 20 Mar 17 nicklas 390
7310 20 Mar 17 nicklas 391   /**
7310 20 Mar 17 nicklas 392     Load a minimal directory tree that includes at least all the specified directories 
7310 20 Mar 17 nicklas 393     and their parent directories all the way up to the root or as far as the logged in user
7310 20 Mar 17 nicklas 394     has read permission.
7310 20 Mar 17 nicklas 395     @param dc A DbControl for accessing the database
7310 20 Mar 17 nicklas 396     @param include Include settings for loading sub directories
7310 20 Mar 17 nicklas 397     @param directories An array of directories
7310 20 Mar 17 nicklas 398     @return A <code>Map</code> which maps a directory to a 
7310 20 Mar 17 nicklas 399       list of it's sub-directories. The map is guaranteed to contain an entry for each of
7310 20 Mar 17 nicklas 400       the given directories, but the list may be empty if there are no subdirectories
7310 20 Mar 17 nicklas 401     @throws BaseException If there is an error
7310 20 Mar 17 nicklas 402     @since 3.11
7310 20 Mar 17 nicklas 403   */
7310 20 Mar 17 nicklas 404   public static Map<Directory, List<Directory>> loadMinimalDirectoryTree(DbControl dc, Collection<Include> include, Directory... directories)
7310 20 Mar 17 nicklas 405     throws InvalidDataException, BaseException
7310 20 Mar 17 nicklas 406   {
4794 02 Mar 09 nicklas 407     Map<Directory, List<Directory>> tree = new HashMap<Directory, List<Directory>>();
4794 02 Mar 09 nicklas 408
4794 02 Mar 09 nicklas 409     // 1. Move up the directory tree as far as possible
4794 02 Mar 09 nicklas 410     //    storing all directories that we pass
4794 02 Mar 09 nicklas 411     Set<Integer> all = new HashSet<Integer>();
4794 02 Mar 09 nicklas 412     for (Directory d : directories)
4794 02 Mar 09 nicklas 413     {
5014 25 Jun 09 martin 414       if (d == null)
5014 25 Jun 09 martin 415       {
5014 25 Jun 09 martin 416         /* #### CONTINUE-STATEMENT #### */
5014 25 Jun 09 martin 417         continue;
5014 25 Jun 09 martin 418       }
4794 02 Mar 09 nicklas 419       tree.put(d, new LinkedList<Directory>());
4794 02 Mar 09 nicklas 420       all.add(d.getId());
4794 02 Mar 09 nicklas 421       try
4794 02 Mar 09 nicklas 422       {
4794 02 Mar 09 nicklas 423         Directory parent = d.getParent();
4794 02 Mar 09 nicklas 424         while (parent != null)
4794 02 Mar 09 nicklas 425         {
4794 02 Mar 09 nicklas 426           if (all.add(parent.getId()))
4794 02 Mar 09 nicklas 427           {
4794 02 Mar 09 nicklas 428             parent = parent.getParent();
4794 02 Mar 09 nicklas 429           }
4794 02 Mar 09 nicklas 430           else
4794 02 Mar 09 nicklas 431           {
4794 02 Mar 09 nicklas 432             // break if the directory has already been added
4794 02 Mar 09 nicklas 433             parent = null;
4794 02 Mar 09 nicklas 434           }
4794 02 Mar 09 nicklas 435         }
4794 02 Mar 09 nicklas 436       }
4794 02 Mar 09 nicklas 437       catch (PermissionDeniedException ex)
4794 02 Mar 09 nicklas 438       {}
4794 02 Mar 09 nicklas 439     }
4794 02 Mar 09 nicklas 440
4794 02 Mar 09 nicklas 441     // 2. Query that loads the subdirectories to all directories that we passed
4794 02 Mar 09 nicklas 442     ItemQuery<Directory> query = Directory.getQuery();
4794 02 Mar 09 nicklas 443     query.restrict(
4794 02 Mar 09 nicklas 444       Restrictions.in(
4794 02 Mar 09 nicklas 445         Hql.property("parent"),
4794 02 Mar 09 nicklas 446         Expressions.parameter("parents")
4794 02 Mar 09 nicklas 447       )
4794 02 Mar 09 nicklas 448     );
4794 02 Mar 09 nicklas 449     query.order(Orders.asc(Hql.property("name")));
7310 20 Mar 17 nicklas 450     if (include != null && include.size() > 0)
7310 20 Mar 17 nicklas 451     {
7310 20 Mar 17 nicklas 452       query.setIncludes(include);
7310 20 Mar 17 nicklas 453     }
4794 02 Mar 09 nicklas 454     query.setParameter("parents", all, Type.INT);
4794 02 Mar 09 nicklas 455     
4794 02 Mar 09 nicklas 456     ItemResultIterator<Directory> it = query.iterate(dc);
4794 02 Mar 09 nicklas 457     while (it.hasNext())
4794 02 Mar 09 nicklas 458     {
4794 02 Mar 09 nicklas 459       Directory child = it.next();
4794 02 Mar 09 nicklas 460       Directory parent = child.getParent();
4794 02 Mar 09 nicklas 461       if (!tree.containsKey(parent))
4794 02 Mar 09 nicklas 462       {
4794 02 Mar 09 nicklas 463         tree.put(parent, new LinkedList<Directory>());
4794 02 Mar 09 nicklas 464       }
4794 02 Mar 09 nicklas 465       tree.get(parent).add(child);
4794 02 Mar 09 nicklas 466     }
4794 02 Mar 09 nicklas 467     return tree;
4794 02 Mar 09 nicklas 468   }
4794 02 Mar 09 nicklas 469   
4794 02 Mar 09 nicklas 470   
4794 02 Mar 09 nicklas 471   /**
3832 15 Oct 07 nicklas 472      Find files matching a given filter in a directory. If the filter
3832 15 Oct 07 nicklas 473      returns sub-directories, this method will check those directories too.
3832 15 Oct 07 nicklas 474      Use, for example, the {@link RegexpFileFilter} to look for files
3832 15 Oct 07 nicklas 475      matching a regular expression.
3832 15 Oct 07 nicklas 476      
3832 15 Oct 07 nicklas 477       @param directory The parent directory to search in
3832 15 Oct 07 nicklas 478       @param filter A filter which specifies which files to find, or null
3832 15 Oct 07 nicklas 479         to find all files
3698 21 Aug 07 martin 480       @return A List with the files in directory
3832 15 Oct 07 nicklas 481         that matches the filter, or null if the specified directory doesn't
3832 15 Oct 07 nicklas 482         exists or isn't a directory
3698 21 Aug 07 martin 483       @since 2.5
3832 15 Oct 07 nicklas 484       @see RegexpFileFilter
3698 21 Aug 07 martin 485   */
3832 15 Oct 07 nicklas 486   public static List<File> findFiles(File directory, FileFilter filter)
3698 21 Aug 07 martin 487   {  
3832 15 Oct 07 nicklas 488     if (!directory.exists() || !directory.isDirectory())
3700 23 Aug 07 martin 489     {
3832 15 Oct 07 nicklas 490       return null;
3700 23 Aug 07 martin 491     }
3698 21 Aug 07 martin 492     
3832 15 Oct 07 nicklas 493     // To hold the result
3832 15 Oct 07 nicklas 494     List<File> searchResult = new LinkedList<File>();
3832 15 Oct 07 nicklas 495     
3832 15 Oct 07 nicklas 496     // Temporary holds the directories too look in
3832 15 Oct 07 nicklas 497     List<File> directories = new LinkedList<File>();
3832 15 Oct 07 nicklas 498     directories.add(directory);
3832 15 Oct 07 nicklas 499     
3832 15 Oct 07 nicklas 500     while (directories.size() > 0)
3698 21 Aug 07 martin 501     {
4120 04 Feb 08 nicklas 502       ThreadSignalHandler.checkInterrupted();
3832 15 Oct 07 nicklas 503       File dir = directories.remove(0);
3832 15 Oct 07 nicklas 504       File[] files = dir.listFiles(filter);
3832 15 Oct 07 nicklas 505       for (File f : files)
3698 21 Aug 07 martin 506       {
4120 04 Feb 08 nicklas 507         ThreadSignalHandler.checkInterrupted();
3832 15 Oct 07 nicklas 508         if (f.isDirectory())
3698 21 Aug 07 martin 509         {
3832 15 Oct 07 nicklas 510           directories.add(f);
3698 21 Aug 07 martin 511         }
3832 15 Oct 07 nicklas 512         else
3832 15 Oct 07 nicklas 513         {
3832 15 Oct 07 nicklas 514           searchResult.add(f);
3832 15 Oct 07 nicklas 515         }
3698 21 Aug 07 martin 516       }
3698 21 Aug 07 martin 517     }
3698 21 Aug 07 martin 518     return searchResult;
3698 21 Aug 07 martin 519   }
4977 23 Jun 09 nicklas 520   
4977 23 Jun 09 nicklas 521   /**
4977 23 Jun 09 nicklas 522     Deletes all empty subdirectories and, optionally, also
4977 23 Jun 09 nicklas 523     the root directory.
4977 23 Jun 09 nicklas 524     @param root The root directory to start looking in
4977 23 Jun 09 nicklas 525     @param deleteRoot TRUE to also delete the root directory
4977 23 Jun 09 nicklas 526       if it is empty, FALSE to leave it
4977 23 Jun 09 nicklas 527     @return The number of deleted directories
4977 23 Jun 09 nicklas 528     @since 2.13
4977 23 Jun 09 nicklas 529   */
4977 23 Jun 09 nicklas 530   public static int deleteEmptyDirectories(File root, boolean deleteRoot)
4977 23 Jun 09 nicklas 531   {
4977 23 Jun 09 nicklas 532     // Step 1 - Create a list with all subdirectories
4977 23 Jun 09 nicklas 533     List<File> allDirs = new LinkedList<File>();
4977 23 Jun 09 nicklas 534     List<File> unchecked = new LinkedList<File>();
4977 23 Jun 09 nicklas 535     unchecked.add(root);
4977 23 Jun 09 nicklas 536     while (unchecked.size() > 0)
4977 23 Jun 09 nicklas 537     {
4977 23 Jun 09 nicklas 538       ThreadSignalHandler.checkInterrupted();
4977 23 Jun 09 nicklas 539       File dir = unchecked.remove(0);
4977 23 Jun 09 nicklas 540       File[] files = dir.listFiles();
4977 23 Jun 09 nicklas 541       if (files != null)
4977 23 Jun 09 nicklas 542       {
4977 23 Jun 09 nicklas 543         for (File f : files)
4977 23 Jun 09 nicklas 544         {
4977 23 Jun 09 nicklas 545           if (f.isDirectory()) unchecked.add(f);
4977 23 Jun 09 nicklas 546         }
4977 23 Jun 09 nicklas 547         // Important! Add the directory first in the list!
4977 23 Jun 09 nicklas 548         if (deleteRoot || !dir.equals(root)) allDirs.add(0, dir);
4977 23 Jun 09 nicklas 549       }
4977 23 Jun 09 nicklas 550     }
4977 23 Jun 09 nicklas 551     
4977 23 Jun 09 nicklas 552     // Step 2 - Remove empty directories
4977 23 Jun 09 nicklas 553     int numDeleted = 0;
4977 23 Jun 09 nicklas 554     for (File subDir : allDirs)
4977 23 Jun 09 nicklas 555     {
4977 23 Jun 09 nicklas 556       ThreadSignalHandler.checkInterrupted();
4977 23 Jun 09 nicklas 557       File[] files = subDir.listFiles();
4977 23 Jun 09 nicklas 558       if (files.length == 0) 
4977 23 Jun 09 nicklas 559       {
4977 23 Jun 09 nicklas 560         if (subDir.delete()) numDeleted++;
4977 23 Jun 09 nicklas 561       }
4977 23 Jun 09 nicklas 562     }
4977 23 Jun 09 nicklas 563     return numDeleted;
4977 23 Jun 09 nicklas 564   }
5010 23 Jun 09 nicklas 565   
5010 23 Jun 09 nicklas 566   /**
5010 23 Jun 09 nicklas 567     Do something recursively to all files and sub-directories in
5010 23 Jun 09 nicklas 568     specified root directory. If no directory filter is specified,
5010 23 Jun 09 nicklas 569     all subdirectories are processed, otherwise only those directories
5010 23 Jun 09 nicklas 570     were the {@link Filter#evaluate(Object)} method returns TRUE.
5010 23 Jun 09 nicklas 571     <p>
5010 23 Jun 09 nicklas 572     Files are only loaded if a file filter is specified. The
5010 23 Jun 09 nicklas 573     return value of the <code>evaluate</code> method is ignored.
5010 23 Jun 09 nicklas 574     <p>
5010 23 Jun 09 nicklas 575     The files and directories may be processed in any order.
5010 23 Jun 09 nicklas 576     <p>
5010 23 Jun 09 nicklas 577     NOTE! The intention of this method is that the filter should
5010 23 Jun 09 nicklas 578     have have a "side-effect" that does some work on the files/
5010 23 Jun 09 nicklas 579     sub-directories.
5010 23 Jun 09 nicklas 580     
5010 23 Jun 09 nicklas 581     @param dc A DbControl to use for database access
5010 23 Jun 09 nicklas 582     @param root The root directory
5010 23 Jun 09 nicklas 583     @param dirFilter A filter that selects/rejects sub-directories
5010 23 Jun 09 nicklas 584     @param fileFilter  A filter that does something with the file
5010 23 Jun 09 nicklas 585     @since 2.13
5010 23 Jun 09 nicklas 586   */
5010 23 Jun 09 nicklas 587   public static void doRecursively(DbControl dc, Directory root, 
5010 23 Jun 09 nicklas 588       Filter<? super Directory> dirFilter, Filter<? super net.sf.basedb.core.File> fileFilter)
5010 23 Jun 09 nicklas 589   {
5010 23 Jun 09 nicklas 590     // Create the file/directory queries
5010 23 Jun 09 nicklas 591     ItemQuery<Directory> dirQuery = Directory.getQuery();
5010 23 Jun 09 nicklas 592     dirQuery.restrict(
5010 23 Jun 09 nicklas 593         Restrictions.eq(
5010 23 Jun 09 nicklas 594           Hql.property("parent.id"), 
5010 23 Jun 09 nicklas 595           Expressions.parameter("parent")
5010 23 Jun 09 nicklas 596         )
5010 23 Jun 09 nicklas 597       );
5010 23 Jun 09 nicklas 598     dirQuery.include(Include.ALL);
5010 23 Jun 09 nicklas 599     
5010 23 Jun 09 nicklas 600     ItemQuery<net.sf.basedb.core.File> fileQuery = net.sf.basedb.core.File.getQuery();
5010 23 Jun 09 nicklas 601     fileQuery.restrict(
5010 23 Jun 09 nicklas 602         Restrictions.eq(
5010 23 Jun 09 nicklas 603           Hql.property("directory.id"), 
5010 23 Jun 09 nicklas 604           Expressions.parameter("directory")
5010 23 Jun 09 nicklas 605         )
5010 23 Jun 09 nicklas 606       );
5010 23 Jun 09 nicklas 607     fileQuery.include(Include.ALL);
5010 23 Jun 09 nicklas 608     
5010 23 Jun 09 nicklas 609     // Load the staring directories
5010 23 Jun 09 nicklas 610     List<Directory> directories = new LinkedList<Directory>();
5010 23 Jun 09 nicklas 611     directories.add(root);
5010 23 Jun 09 nicklas 612     
5010 23 Jun 09 nicklas 613     while (directories.size() > 0)
5010 23 Jun 09 nicklas 614     {
5010 23 Jun 09 nicklas 615       ThreadSignalHandler.checkInterrupted();
5010 23 Jun 09 nicklas 616       Directory d = directories.remove(0);
5010 23 Jun 09 nicklas 617       if (dirFilter == null || dirFilter.evaluate(d)) 
5010 23 Jun 09 nicklas 618       {
5010 23 Jun 09 nicklas 619         dirQuery.setParameter("parent", d.getId(), Type.INT);
5010 23 Jun 09 nicklas 620         directories.addAll(dirQuery.list(dc));
5010 23 Jun 09 nicklas 621       }
5010 23 Jun 09 nicklas 622       if (fileFilter != null)
5010 23 Jun 09 nicklas 623       {
5010 23 Jun 09 nicklas 624         fileQuery.setParameter("directory", d.getId(), Type.INT);
5010 23 Jun 09 nicklas 625         for (net.sf.basedb.core.File f : fileQuery.list(dc))
5010 23 Jun 09 nicklas 626         {
5010 23 Jun 09 nicklas 627           fileFilter.evaluate(f);
5010 23 Jun 09 nicklas 628         }
5010 23 Jun 09 nicklas 629       }
5010 23 Jun 09 nicklas 630     }
5010 23 Jun 09 nicklas 631   }
5010 23 Jun 09 nicklas 632   
5219 18 Jan 10 nicklas 633   /**
5219 18 Jan 10 nicklas 634     Creates a temporary directory. This method uses the built-in {@link
5219 18 Jan 10 nicklas 635     File#createTempFile(String, String, File)} method to first crete a temporary
5219 18 Jan 10 nicklas 636     file, that is then deleted and made into a directory instead. This should
5219 18 Jan 10 nicklas 637     ensure that a new empty directory is created each time this method is called.
5219 18 Jan 10 nicklas 638     The "temporary" nature of this method doesn't involve cleaning up or removing
5219 18 Jan 10 nicklas 639     the created directory. It is the responsibility of the calling code to remove
5219 18 Jan 10 nicklas 640     every file and subdirectory as well as the returned directory. This can
5219 18 Jan 10 nicklas 641     for example be done with the {@link #deleteTempDirectory(File)} method.
5219 18 Jan 10 nicklas 642     
5219 18 Jan 10 nicklas 643     @param prefix A prefix that is used in the directory name 
5219 18 Jan 10 nicklas 644       it must be at least 3 characters long.
5219 18 Jan 10 nicklas 645     @param suffix An optional suffix for the directory name
5219 18 Jan 10 nicklas 646     @param directory An optional parent directory in which the temporary
5219 18 Jan 10 nicklas 647       directory should be created, or null to use the system temporary 
5219 18 Jan 10 nicklas 648       directory
5219 18 Jan 10 nicklas 649     @return A reference to the created directory
5219 18 Jan 10 nicklas 650     @throws IOException If there is a problem with the directory creation
5219 18 Jan 10 nicklas 651     @since 2.15
5219 18 Jan 10 nicklas 652     @see File#createTempFile(String, String, File)
5219 18 Jan 10 nicklas 653   */
5219 18 Jan 10 nicklas 654   public static File createTempDirectory(String prefix, String suffix, File directory)
5219 18 Jan 10 nicklas 655     throws IOException
5219 18 Jan 10 nicklas 656   {
5219 18 Jan 10 nicklas 657     File tmpDir = File.createTempFile(prefix, suffix, directory);
5219 18 Jan 10 nicklas 658     if (!tmpDir.delete() || !tmpDir.mkdir())
5219 18 Jan 10 nicklas 659     {
5219 18 Jan 10 nicklas 660       throw new IOException("Could not create directory: " + tmpDir.getAbsolutePath());
5219 18 Jan 10 nicklas 661     }
5219 18 Jan 10 nicklas 662     return tmpDir;
5219 18 Jan 10 nicklas 663   }
5219 18 Jan 10 nicklas 664   
5219 18 Jan 10 nicklas 665   /**
5219 18 Jan 10 nicklas 666     Recursively delete all files and subdirectories within a given
5219 18 Jan 10 nicklas 667     directory and the also the given directory itself.
5219 18 Jan 10 nicklas 668
5219 18 Jan 10 nicklas 669     @param root The root directory to start looking in
5219 18 Jan 10 nicklas 670     @return The number of deleted files and directories
5219 18 Jan 10 nicklas 671     @since 2.15
5219 18 Jan 10 nicklas 672   */
5219 18 Jan 10 nicklas 673   public static int deleteTempDirectory(File root)
5219 18 Jan 10 nicklas 674   {
5219 18 Jan 10 nicklas 675     int numDeleted = 0;
5219 18 Jan 10 nicklas 676
5219 18 Jan 10 nicklas 677     // Step 1 - Remove the files and create a list with all subdirectories
5219 18 Jan 10 nicklas 678     List<File> allDirs = new LinkedList<File>();
5219 18 Jan 10 nicklas 679     List<File> unchecked = new LinkedList<File>();
5219 18 Jan 10 nicklas 680     unchecked.add(root);
5219 18 Jan 10 nicklas 681     while (unchecked.size() > 0)
5219 18 Jan 10 nicklas 682     {
5219 18 Jan 10 nicklas 683       ThreadSignalHandler.checkInterrupted();
5219 18 Jan 10 nicklas 684       File dir = unchecked.remove(0);
5219 18 Jan 10 nicklas 685       File[] files = dir.listFiles();
5219 18 Jan 10 nicklas 686       if (files != null)
5219 18 Jan 10 nicklas 687       {
5219 18 Jan 10 nicklas 688         // Important! Add the directory first in the list!
5219 18 Jan 10 nicklas 689         allDirs.add(0, dir);
5219 18 Jan 10 nicklas 690         for (File f : files)
5219 18 Jan 10 nicklas 691         {
5219 18 Jan 10 nicklas 692           if (f.isDirectory()) 
5219 18 Jan 10 nicklas 693           {
5219 18 Jan 10 nicklas 694             unchecked.add(f);
5219 18 Jan 10 nicklas 695           }
5219 18 Jan 10 nicklas 696           else
5219 18 Jan 10 nicklas 697           {
5219 18 Jan 10 nicklas 698             if (f.delete()) numDeleted++;
5219 18 Jan 10 nicklas 699           }
5219 18 Jan 10 nicklas 700         }
5219 18 Jan 10 nicklas 701       }
5219 18 Jan 10 nicklas 702     }
5219 18 Jan 10 nicklas 703     
5219 18 Jan 10 nicklas 704     // Step 2 - Remove the directories
5219 18 Jan 10 nicklas 705     for (File subDir : allDirs)
5219 18 Jan 10 nicklas 706     {
5219 18 Jan 10 nicklas 707       ThreadSignalHandler.checkInterrupted();
5219 18 Jan 10 nicklas 708       if (subDir.delete()) numDeleted++;
5219 18 Jan 10 nicklas 709     }
5219 18 Jan 10 nicklas 710     return numDeleted;
5219 18 Jan 10 nicklas 711   }
5219 18 Jan 10 nicklas 712   
5374 03 Aug 10 nicklas 713   /**
5374 03 Aug 10 nicklas 714     Upload multiple files to BASE. 
5374 03 Aug 10 nicklas 715     @param toDir The directory in the BASE file system that the files should
5374 03 Aug 10 nicklas 716       be uploaded to
5374 03 Aug 10 nicklas 717     @param fromDir The directory in the local file system to scan for files 
5374 03 Aug 10 nicklas 718       to upload if the directory doesn't exists or is empty an empty list is
5374 03 Aug 10 nicklas 719       returned
5374 03 Aug 10 nicklas 720     @param filter An optional filter that decides which files that should
5374 03 Aug 10 nicklas 721       be uploaded or not, if null all files, including subdirectories, are
5374 03 Aug 10 nicklas 722       uploaded
5374 03 Aug 10 nicklas 723     @param overwrite TRUE if existing files should be overwritten, FALSE to
5374 03 Aug 10 nicklas 724       only overwrite files that have been marked for removal
5374 03 Aug 10 nicklas 725     @param progress An optional progress reporter
5374 03 Aug 10 nicklas 726     @return A list with the new file items that were created
5374 03 Aug 10 nicklas 727     @since 2.16
5374 03 Aug 10 nicklas 728   */
5374 03 Aug 10 nicklas 729   public static List<net.sf.basedb.core.File> uploadFiles(DbControl dc, Directory toDir, 
5374 03 Aug 10 nicklas 730     File fromDir, final FileFilter filter, boolean overwrite, ProgressReporter progress)
5374 03 Aug 10 nicklas 731     throws IOException
5374 03 Aug 10 nicklas 732   {
5374 03 Aug 10 nicklas 733     if (!fromDir.exists() || !fromDir.isDirectory())
5374 03 Aug 10 nicklas 734     {
5374 03 Aug 10 nicklas 735       return Collections.emptyList();
5374 03 Aug 10 nicklas 736     }
5374 03 Aug 10 nicklas 737   
5374 03 Aug 10 nicklas 738     // All files to upload in BASE and native representation
5374 03 Aug 10 nicklas 739     List<net.sf.basedb.core.File> uploaded = new ArrayList<net.sf.basedb.core.File>();
5374 03 Aug 10 nicklas 740     List<File> fileToUpload = new ArrayList<File>();
5374 03 Aug 10 nicklas 741
5374 03 Aug 10 nicklas 742     // Temporary holds the directories too look in
5374 03 Aug 10 nicklas 743     List<File> directories = new LinkedList<File>();
5374 03 Aug 10 nicklas 744     Map<File, Directory> subdirs = new HashMap<File, Directory>();
5374 03 Aug 10 nicklas 745     directories.add(fromDir);
5374 03 Aug 10 nicklas 746     subdirs.put(fromDir, toDir);
5374 03 Aug 10 nicklas 747     
5374 03 Aug 10 nicklas 748     // First step: Scan the directories to find all files to upload
5374 03 Aug 10 nicklas 749     // This step will create BASE File and Directory items
5374 03 Aug 10 nicklas 750     long totalBytes = 0;
5374 03 Aug 10 nicklas 751     while (directories.size() > 0)
5374 03 Aug 10 nicklas 752     {
5374 03 Aug 10 nicklas 753       ThreadSignalHandler.checkInterrupted();
5374 03 Aug 10 nicklas 754       File dir = directories.remove(0);
5374 03 Aug 10 nicklas 755       Directory uploadDir = subdirs.get(dir);
5374 03 Aug 10 nicklas 756       
5374 03 Aug 10 nicklas 757       File[] files = dir.listFiles(filter);
5374 03 Aug 10 nicklas 758       for (File f : files)
5374 03 Aug 10 nicklas 759       {
5374 03 Aug 10 nicklas 760         if (f.isDirectory())
5374 03 Aug 10 nicklas 761         {
5374 03 Aug 10 nicklas 762           // Create subdirectory
5374 03 Aug 10 nicklas 763           directories.add(f);
5374 03 Aug 10 nicklas 764           Directory newDirectory = Directory.getNew(dc, uploadDir);
5374 03 Aug 10 nicklas 765           newDirectory.setName(f.getName());
5374 03 Aug 10 nicklas 766           dc.saveItem(newDirectory);
5374 03 Aug 10 nicklas 767           subdirs.put(f, newDirectory);
5374 03 Aug 10 nicklas 768         }
5374 03 Aug 10 nicklas 769         else if (f.isFile())
5374 03 Aug 10 nicklas 770         {
5374 03 Aug 10 nicklas 771           // Upload the file
5374 03 Aug 10 nicklas 772           net.sf.basedb.core.File baseFile = net.sf.basedb.core.File.getFile(dc, uploadDir, f.getName(), true);
5374 03 Aug 10 nicklas 773           if (!baseFile.isInDatabase())
5374 03 Aug 10 nicklas 774           {
5374 03 Aug 10 nicklas 775             dc.saveItem(baseFile);
5374 03 Aug 10 nicklas 776           }
5374 03 Aug 10 nicklas 777           if (overwrite || !baseFile.isInDatabase() || baseFile.isRemoved())
5374 03 Aug 10 nicklas 778           {
5374 03 Aug 10 nicklas 779             uploaded.add(baseFile);
5374 03 Aug 10 nicklas 780             fileToUpload.add(f);
5374 03 Aug 10 nicklas 781             totalBytes += f.length();
5374 03 Aug 10 nicklas 782           }
5374 03 Aug 10 nicklas 783         }
5374 03 Aug 10 nicklas 784       }
5374 03 Aug 10 nicklas 785     }
5374 03 Aug 10 nicklas 786
5374 03 Aug 10 nicklas 787     // Second step: Perform the upload
5374 03 Aug 10 nicklas 788     SimpleAbsoluteProgressReporter aProgress = null;
5374 03 Aug 10 nicklas 789     if (progress != null) aProgress = new SimpleAbsoluteProgressReporter(progress, totalBytes);
5374 03 Aug 10 nicklas 790     long numBytes = 0;
5374 03 Aug 10 nicklas 791     for (int i = 0; i < fileToUpload.size(); ++i)
5374 03 Aug 10 nicklas 792     {
5374 03 Aug 10 nicklas 793       ThreadSignalHandler.checkInterrupted();
5374 03 Aug 10 nicklas 794       File f = fileToUpload.get(i);
5374 03 Aug 10 nicklas 795       net.sf.basedb.core.File baseFile = uploaded.get(i);
5374 03 Aug 10 nicklas 796       InputStream in = null;
5374 03 Aug 10 nicklas 797       OutputStream out = null;
5374 03 Aug 10 nicklas 798       try
5374 03 Aug 10 nicklas 799       {
5374 03 Aug 10 nicklas 800         if (aProgress != null) 
5374 03 Aug 10 nicklas 801         {
5374 03 Aug 10 nicklas 802           aProgress.setAbsolute(numBytes, "Uploading: " + f.getName());
5374 03 Aug 10 nicklas 803         }
5374 03 Aug 10 nicklas 804         baseFile.setRemoved(false);
5374 03 Aug 10 nicklas 805         baseFile.setMimeTypeAuto(null, null);
5374 03 Aug 10 nicklas 806         in = FileUtil.getInputStream(f);
5374 03 Aug 10 nicklas 807         out = baseFile.getUploadStream(false);
5374 03 Aug 10 nicklas 808         numBytes += FileUtil.copy(in, out, aProgress);
5374 03 Aug 10 nicklas 809       }
5374 03 Aug 10 nicklas 810       finally
5374 03 Aug 10 nicklas 811       {
5374 03 Aug 10 nicklas 812         FileUtil.close(in);
5374 03 Aug 10 nicklas 813         FileUtil.close(out);
5374 03 Aug 10 nicklas 814       }
5374 03 Aug 10 nicklas 815     }
5374 03 Aug 10 nicklas 816     return uploaded;
5374 03 Aug 10 nicklas 817   }
5374 03 Aug 10 nicklas 818   
3698 21 Aug 07 martin 819 }