]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils/src/org/simantics/utils/FileUtils.java
Hardening of DB index integrity
[simantics/platform.git] / bundles / org.simantics.utils / src / org / simantics / utils / FileUtils.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.utils;
13
14 import java.io.BufferedReader;
15 import java.io.Closeable;
16 import java.io.File;
17 import java.io.FileFilter;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 import java.io.RandomAccessFile;
26 import java.io.UnsupportedEncodingException;
27 import java.net.URI;
28 import java.net.URL;
29 import java.nio.channels.FileChannel;
30 import java.nio.charset.Charset;
31 import java.nio.file.FileVisitResult;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.SimpleFileVisitor;
35 import java.nio.file.StandardCopyOption;
36 import java.nio.file.attribute.BasicFileAttributes;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Deque;
40 import java.util.LinkedList;
41 import java.util.Random;
42 import java.util.zip.DataFormatException;
43 import java.util.zip.Deflater;
44 import java.util.zip.Inflater;
45 import java.util.zip.ZipEntry;
46 import java.util.zip.ZipException;
47 import java.util.zip.ZipInputStream;
48 import java.util.zip.ZipOutputStream;
49
50 import org.simantics.databoard.Bindings;
51 import org.simantics.databoard.adapter.AdaptException;
52 import org.simantics.databoard.adapter.Adapter;
53 import org.simantics.databoard.adapter.AdapterConstructionException;
54 import org.simantics.databoard.binding.Binding;
55 import org.simantics.databoard.type.Datatype;
56 import org.simantics.utils.bytes.LEInt;
57 import org.simantics.utils.strings.FileNameUtils;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * Utilities for common file operations.
63  *
64  * see StreamUtil in databoard for Input/OutputStream reading/writing utils
65  * @see FileNameUtils for more utils
66  * 
67  * @author Toni Kalajainen
68  * @author Tuukka Lehtonen
69  */
70 public class FileUtils {
71
72         private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class);
73
74         /**
75          * Create escaped filename
76          * 
77          * @param name any string
78          * @return file compatible string
79          */
80         public static String escapeFileName(String name) {
81                 try {
82                         return java.net.URLEncoder.encode(name, "UTF-8");
83                 } catch (UnsupportedEncodingException e) {
84                         // never expected
85                         throw new RuntimeException(e);
86                 }
87         }
88         
89         /**
90          * Unescape filename into string 
91          * 
92          * @param filename 
93          * @return any string
94          */
95         public static String unescapeFileName(String filename) {
96                 try {
97                         return java.net.URLDecoder.decode(filename, "UTF-8");
98                 } catch (UnsupportedEncodingException e) {
99                         // never expected
100                         throw new RuntimeException(e);
101                 }
102         }
103
104     public static File ensureParentDirectoryExists(String path) throws IOException {
105         return ensureParentDirectoryExists(new File(path));
106     }
107
108     /**
109      * Ensures the parent directory pointed by the specified file path exists as a
110      * directory.
111      * 
112      * @param path the directory whose existence is to be ensured
113      * @return the requested directory
114      * @throws IOException if the specified directory cannot be created or
115      *         already exists as a file
116      */
117     public static File ensureParentDirectoryExists(File path) throws IOException {
118         return ensureDirectoryExists(path.getParentFile());
119     }
120         
121         
122     public static File ensureDirectoryExists(String path) throws IOException {
123         return ensureDirectoryExists(new File(path));
124     }
125
126     /**
127      * Ensures the directory pointed by the specified file path exists as a
128      * directory.
129      * 
130      * @param path the directory whose existence is to be ensured
131      * @return the requested directory
132      * @throws IOException if the specified directory cannot be created or
133      *         already exists as a file
134      */
135     public static File ensureDirectoryExists(File path) throws IOException {
136         if (path.isDirectory())
137             // Already exists, everything OK.
138             return path;
139
140         if (path.exists())
141             // Path is not a directory but it exists, fail!
142             throw new IOException("file '" + path + "', already exists but it is not a directory");
143
144         path.mkdirs();
145         if (!path.exists())
146             // Path is not a directory but it exists, fail!
147             throw new IOException("could not create directory '" + path + "' for an unknown reason");
148
149         // Directory created OK.
150         return path;
151     }
152
153     /**
154      * Copies the contents of a source file to the destination file.
155      * 
156      * @param sourceFile the source file descriptor
157      * @param destFile the destination file descriptor
158      * @throws IOException when anything IO-related goes wrong during the copy
159      */
160     public static void copyFile(File sourceFile, File destFile) throws IOException {
161         if (!destFile.exists()) {
162             destFile.createNewFile();
163         }
164
165         FileInputStream fis = null;
166         FileOutputStream fos = null;
167         try {
168             fis = new FileInputStream(sourceFile);
169             fos = new FileOutputStream(destFile);
170             FileChannel source = fis.getChannel();
171             FileChannel destination = fos.getChannel();
172
173             long count = 0;
174             long size = source.size();
175             while (count < size) {
176                 count += destination.transferFrom(source, count, size - count);
177             }
178         } finally {
179             if (fis != null) {
180                 uncheckedClose(fis);
181             }
182             if (fos != null) {
183                 uncheckedClose(fos);
184             }
185         }
186     }
187     
188     /**
189      * Copy file or dir recursively.
190      * 
191      * @param src
192      * @param dst
193      * @throws IOException
194      */
195     public static void copy(File src, File dst) throws IOException
196     {
197         if (src.isDirectory()) {
198                 if(!dst.exists()) dst.mkdir();
199
200                 for (String file : src.list()) {
201                         File srcFile = new File(src, file);
202                         File dstFile = new File(dst, file);
203                         copy(srcFile,dstFile);
204                 }
205         } else {
206                 copyFile(src, dst);
207         }
208     }
209     
210
211 //    public static File createTempFileFromResource(URL sourceUrl) throws IOException {
212 //        sourceUrl = FileLocator.resolve(sourceUrl);
213 //        File sourceFile;
214 //        try {
215 //            if (sourceUrl.getProtocol().equalsIgnoreCase("file"))
216 //                sourceFile = new File(sourceUrl.getPath());
217 //            else
218 //                sourceFile = new File(sourceUrl.toURI());
219 //        } catch (URISyntaxException e) {
220 //            throw new RuntimeException(e);
221 //        }
222 //
223 //        File tempFile = File.createTempFile("tmp", ".tmp");
224 //        FileUtils.copyFile(sourceFile, tempFile);
225 //        return tempFile;
226 //    }
227
228     /**
229      * Reads entire binary file
230      * @param file file
231      * @return contents of binary file
232      * @throws IOException on i/o problems
233      */
234     public static byte[] readFile(File file)
235     throws IOException
236     {
237         try (FileInputStream fis = new FileInputStream(file)) {
238             long size = file.length();
239             if (size>Integer.MAX_VALUE)
240                 throw new IOException("File too big");
241             int len = (int) size;
242             byte data [] = new byte[len];
243             int pos = 0;
244
245             while (pos<size) {
246                 int read = fis.read(data, pos, len-pos);
247                 pos += read;
248             }
249             return data;
250         }
251     }
252
253     /**
254      * Creates and writes a binary file
255      * @param file file
256      * @param data data
257      * @throws IOException on i/o problems
258      */
259     public static void writeFile(File file, byte[] data)
260     throws IOException
261     {
262 //        file.createNewFile();
263 //        file.setWritable(true);
264         try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
265             raf.setLength(data.length);
266             raf.seek(0);
267             raf.write(data);
268         }
269     }
270
271
272     /**
273      * Fetch the entire contents of the input stream text file, and return it in
274      * a String. This style of implementation does not throw Exceptions to the
275      * caller.
276      * 
277      * @param stream the stream to completely read in
278      */
279     public static String getContents(InputStream stream) throws IOException {
280         return getContents(stream, Charset.defaultCharset());
281     }
282
283     /**
284      * Fetch the entire contents of the input stream text file, and return it in
285      * a String. This style of implementation does not throw Exceptions to the
286      * caller.
287      * 
288      * @param stream the stream to completely read in
289      */
290     public static String getContents(InputStream stream, Charset charset, String lineSeparator) throws IOException {
291         StringBuilder contents = new StringBuilder();
292         BufferedReader input = null;
293         try {
294             input = new BufferedReader(new InputStreamReader(stream, charset));
295
296             String line = null;
297             while ((line = input.readLine()) != null) {
298                 contents.append(line);
299                 contents.append(lineSeparator);
300             }
301         } finally {
302             try {
303                 if (input != null) {
304                     // flush and close both "input" and its underlying
305                     // FileReader
306                     input.close();
307                 }
308             } catch (IOException ex) {
309                 ex.printStackTrace();
310             }
311         }
312         return contents.toString();
313     }
314
315     public static String getContents(File f) throws IOException {
316         return getContents(f, Charset.defaultCharset());
317     }
318     
319     public static String getContents(File f, Charset charset) throws IOException {
320         return getContents(new FileInputStream(f), charset);
321     }
322
323     public static String getContents(InputStream stream, Charset charset) throws IOException {
324         return getContents(stream, charset, System.getProperty("line.separator"));
325     }
326
327     public static String getContents(InputStream stream, String lineSeparator) throws IOException {
328         return getContents(stream, Charset.defaultCharset(), lineSeparator);
329     }
330     
331     /**
332      * Fetch the entire contents of a text file, and return it in a String. This
333      * style of implementation does not throw Exceptions to the caller.
334      * 
335      * @param sourcePath is a file which already exists and can be read.
336      */
337     public static String getContents(String sourcePath) throws IOException {
338         return getContents(sourcePath, Charset.defaultCharset(), System.getProperty("line.separator"));
339     }
340
341     public static String getContents(String sourcePath, String lineSeparator) throws IOException {
342         return getContents(sourcePath, Charset.defaultCharset(), lineSeparator);
343     }
344
345     public static String getContents(String sourcePath, Charset charset) throws IOException {
346         return getContents(sourcePath, charset, System.getProperty("line.separator"));
347     }
348
349     /**
350      * Fetch the entire contents of a text file, and return it in a String. This
351      * style of implementation does not throw Exceptions to the caller.
352      * 
353      * @param sourcePath is a file which already exists and can be read.
354      */
355     public static String getContents(String sourcePath, Charset charset, String lineSeparator) throws IOException {
356         FileInputStream input = null;
357         try {
358             input = new FileInputStream(sourcePath);
359             return getContents(input, charset, lineSeparator);
360         } finally {
361             uncheckedClose(input);
362         }
363     }
364
365     /**
366      * Get the contents of a file which is located inside a given bundle.
367      * This works regardless of whether the bundle is jarred or not.
368      * 
369      * @param URL the resource to read in
370      * @return the contents of the resource
371      * @throws IOException if the file is not found or other io errors occur
372      */
373     public static String getContents(URL url) throws IOException {
374         if (url == null)
375             throw new IllegalArgumentException("null URL");
376         InputStream input = null;
377         try {
378             input = url.openStream();
379             return FileUtils.getContents(input);
380         } finally {
381             uncheckedClose(input);
382         }
383     }
384
385         /**
386          * Read a binary file into a java instance. Binary file is a variant, 
387          * there is a filetype in the header of the file. 
388          * If requested binding is not the exact binding of the file, an adapter is tried.
389          * 
390          * @param file file
391          * @param binding content binding
392          * @return instance
393          * @throws IOException 
394          */
395         public static Object readFile(InputStream stream, Binding binding) throws IOException {
396                 Binding datatype_binding = Bindings.getBindingUnchecked( Datatype.class );
397                 Datatype type = (Datatype) Bindings.getSerializerUnchecked( datatype_binding ).deserialize( stream );
398
399                 if (type.equals(binding.type())) {
400                         return Bindings.getSerializerUnchecked( binding ).deserialize(stream);
401                 } else {
402                         try {
403                                 Binding fileContentBinding = Bindings.getMutableBinding(type);
404                                 Adapter adapter = Bindings.adapterFactory.getAdapter(fileContentBinding, binding, true, false);
405                                 Object value = Bindings.getSerializerUnchecked( fileContentBinding ).deserialize(stream);
406                                 return adapter.adapt( value );
407                         } catch (AdapterConstructionException e) {
408                                 throw new IOException(e);
409                         } catch (AdaptException e) {
410                                 throw new IOException(e);
411                         }
412                 }
413         }
414     
415     /**
416      * Deletes all files and sub-directories from the specified directory. If a
417      * file is specified, only that fill will be deleted.
418      * 
419      * @param dir
420      * @throws IOException
421      */
422     public static void deleteAll(File dir) throws IOException {
423         if (dir.isFile()) {
424             dir.delete();
425             return;
426         }
427
428         if (dir.isDirectory()) {
429             File[] fs = dir.listFiles((FileFilter) null);
430             if (fs == null)
431                 return;
432
433             for (File f : fs) {
434                 if (f.isDirectory()) {
435                     deleteAll(f);
436                 } else {
437                     if (!f.delete()) {
438                         throw new IOException("Could not delete file: " + f.getAbsolutePath());
439                     }
440                 }
441             }
442
443             if (!dir.delete()) {
444                 throw new IOException("Could not delete directory: " + dir.getAbsolutePath());
445             }
446         } else if (dir.exists()) {
447             if (!dir.delete()) {
448                 throw new IOException("Could not delete file: " + dir.getAbsolutePath());
449             }
450         }
451     }
452
453     /**
454      * Deletes all files and sub-directories from the specified directory. If a
455      * file is specified, only that fill will be deleted.
456      * 
457      * If the given directory contains files listed in the filter those files are not deleted
458      * 
459      * @param dir
460      *                          directory from where to start the deletion 
461      * @param filter
462      *                          filter containing specific file paths not to delete
463      * @throws IOException
464      */
465     public static void deleteAllWithFilter(File dir, ArrayList<String> filter) throws IOException {
466         if (dir.isFile()) {
467                 if (!filter.contains(dir.getAbsolutePath())) {
468                         dir.delete();
469                         return;                         
470                 }
471         }
472
473         if (dir.isDirectory()) {
474             File[] fs = dir.listFiles((FileFilter) null);
475             if (fs == null)
476                 return;
477
478             for (File f : fs) {
479                 if (f.isDirectory()) {
480                     deleteAllWithFilter(f, filter);
481                 } else {
482                         if (!filter.contains(f.getAbsolutePath())) {
483                         if (!f.delete()) {
484                             throw new IOException("Could not delete file: " + f.getAbsolutePath());
485                         }       
486                         }
487                 }
488             }
489             if (!filter.contains(dir.getAbsolutePath())) {
490                 if (!dir.delete()) {
491                     throw new IOException("Could not delete directory: " + dir.getAbsolutePath());
492                 }               
493             }
494         } else if (dir.exists()) {
495                 if (!filter.contains(dir.getAbsolutePath())) {
496                 if (!dir.delete()) {
497                     throw new IOException("Could not delete file: " + dir.getAbsolutePath());
498                 }
499                 }
500         }
501     }
502     
503     public static ArrayList<String> createFileFilter(File dir, ArrayList<String> filter) {
504         if (filter == null)
505                 filter = new ArrayList<String>();
506         if (dir.isFile()) {
507                 filter.add(dir.getAbsolutePath());
508                 return filter;
509         }
510         
511         if (dir.isDirectory()) {
512             File[] fs = dir.listFiles((FileFilter) null);
513             if (fs == null)
514                 return filter;
515
516             for (File f : fs) {
517                 if (f.isDirectory()) {
518                     createFileFilter(f, filter);
519                 } else {
520                         filter.add(f.getAbsolutePath());
521                 }
522             }
523             filter.add(dir.getAbsolutePath());
524         } else if (dir.exists()) {
525                 filter.add(dir.getAbsolutePath());
526         }
527                 return filter;
528     }
529
530
531     /**
532      * Delete a directory incl. all files and sub-directories by best effort.
533      * Does not throw exceptions if deletion fails, simply tries to delete the
534      * provided directory to the best of Java File API's abilities.
535      * 
536      * @param dir
537      *            directory to delete recursively
538      * @boolean <code>true</code> if all files were successfully deleted,
539      *          <code>false</code> if some or all failed to be deleted
540      */
541     public static boolean deleteDir(File dir) {
542         if (LOGGER.isDebugEnabled())
543             LOGGER.debug("Deleting directory "+dir);
544         boolean result = true;
545
546         if (!dir.isDirectory()) return false;
547         File[] fs = dir.listFiles();
548         if (fs != null) {
549             for (File f : fs) {
550                 if (f.isDirectory()) result &= deleteDir(f);
551                 if (f.isFile()) result &= f.delete();
552             }
553         }
554         boolean ok = dir.delete();
555 //              if (!ok) dir.deleteOnExit();
556         result &= ok;
557         return result;
558     }
559     
560     public static String deleteDirs(File dir) { 
561         if (LOGGER.isDebugEnabled())
562             LOGGER.debug("Deleting directory "+dir);
563         boolean result = true;
564
565         if (!dir.isDirectory()) 
566                 return dir + " is not a directory!";
567         File[] fs = dir.listFiles();
568         if (fs != null) {
569             for (File f : fs) {
570                 //System.out.println("file f :" + f);
571                 if (f.isDirectory()) { 
572                         boolean a = deleteDir(f);
573                         result &= a;
574                 }
575                 if (f.isFile()) {
576                         boolean a = f.delete();
577                         result &= a;
578                 }
579                         
580             }
581         }
582         boolean ok = dir.delete();
583 //              if (!ok) dir.deleteOnExit();
584         result &= ok;
585         return "deleteDirs succesful for " + dir + " : " + result;
586     }
587
588     /**
589      * Closes a stream and ignores any resulting exception. This is useful
590      * when doing stream cleanup in a finally block where secondary exceptions
591      * are not worth logging.
592      */
593     public static void uncheckedClose(Closeable closeable) {
594         try {
595             if (closeable != null)
596                 closeable.close();
597         } catch (IOException e) {
598             //ignore
599         }
600     }
601
602     /**
603      * Extracts the specified source file in the specified bundle into the
604      * specified local directory.
605      * 
606      * @param url the source URL to stream the resource from
607      * @param targetFile the target file to write the resource to
608      * @param deleteOnExit <code>true</code> to use {@link File#deleteOnExit()}
609      *        on the resulting file. Note that this does not guarantee that the
610      *        file is deleted when the JVM exits
611      * @return the resulting file
612      * @throws FileNotFoundException
613      */
614     public static File copyResource(URL url, File targetFile, boolean deleteOnExit) throws IOException, FileNotFoundException {
615         if (url == null)
616             throw new IllegalArgumentException("null url");
617
618         FileOutputStream os = null;
619         InputStream is = null;
620         try {
621             if (targetFile.exists())
622                 targetFile.delete();
623
624             is = url.openStream();
625             int read;
626             byte [] buffer = new byte [16384];
627             os = new FileOutputStream (targetFile);
628             while ((read = is.read (buffer)) != -1) {
629                 os.write(buffer, 0, read);
630             }
631             os.close ();
632             is.close ();
633
634             // Request removal of the extracted files on JVM exit.
635             if (deleteOnExit)
636                 targetFile.deleteOnExit();
637             return targetFile;
638         } finally {
639             FileUtils.uncheckedClose(os);
640             FileUtils.uncheckedClose(is);
641         }
642     }
643
644     /**
645      * Creates the requested location under the system's temporary directories
646      * for temporary data storage.
647      * 
648      * @param pathSegments the path segments that are appended to
649      *        java.io.tmpdir. The path segments musn't contain path separators.
650      * @return the resulting temporary directory as a File object
651      * @throws IOException if anything goes wrong
652      */
653     public static File getOrCreateTemporaryDirectory(boolean requireEmptyDirectory, String... pathSegments) throws IOException {
654         String separator = System.getProperty("file.separator");
655         String tempDir = System.getProperty("java.io.tmpdir");
656         if (tempDir == null)
657             throw new IllegalStateException("'java.io.tmpdir' property is not defined, is the system TMP environment variable defined?");
658
659         if (!hasTrailingSeparator(tempDir))
660             tempDir = tempDir + separator;
661
662         for (int i = 0; i < pathSegments.length-1; ++i) {
663             if (containsSeparator(pathSegments[i]))
664                 throw new IllegalArgumentException("path segments contain path separators: " + Arrays.toString(pathSegments));
665             tempDir = pathSegments[i] + separator;
666         }
667
668         // Find name for an empty or non-existing temporary directory
669         if (pathSegments.length > 0) {
670             String lastSegment = pathSegments[pathSegments.length - 1];
671             String suffix = "";
672             int counter = 0;
673             // This loop will not exit until it succeeds or some error occurs.
674             while (true) {
675                 File temp = new File(tempDir + lastSegment + suffix);
676
677                 if (!temp.exists()) {
678                     // Found non-existent temporary directory!
679                     if (temp.mkdirs()) {
680                         // Ok, everything seems to be fine.
681                         return temp;
682                     }
683                     // For some reason the directory could not be created, lets
684                     // try another name.
685                 } else {
686                     // Found an existing temporary file.
687                     if (temp.isDirectory() && temp.canWrite()) {
688                         if (requireEmptyDirectory) {
689                             String[] files = temp.list();
690                             if (files != null) {
691                                 if (files.length == 0) {
692                                     // Ok, the directory is empty and writable.
693                                     return temp;
694                                 }
695                             }
696                         } else {
697                             return temp;
698                         }
699                         // Either the directory was empty or it could not be
700                         // checked. In any case, don't use the directory.
701                     }
702                 }
703
704                 // Try another directory name if no success with this one.
705                 suffix = Integer.toHexString(counter);
706                 ++counter;
707             }
708         }
709         return new File(tempDir);
710     }
711
712     private static boolean containsSeparator(String s) {
713         return s.contains("/") || s.contains("\\");
714     }
715
716     private static boolean hasTrailingSeparator(String s) {
717         return s.endsWith("/") || s.endsWith("\\");
718     }
719
720     /**
721      * Very simple compression using ZLib
722      * @param input the uncompressed data
723      * @return the compressed data
724      */
725     public static byte[] deflate(byte[] input) {
726         // Compress the bytes
727         Deflater compresser = new Deflater(Deflater.BEST_COMPRESSION);
728         compresser.setInput(input);
729         compresser.finish();
730
731         int bufferSize = input.length<16 ? 16 : input.length;
732         byte buffer[] = new byte[bufferSize];
733         int  bufferPos = 0;
734         do {
735             int compressedDataLength = compresser.deflate(buffer, bufferPos, buffer.length-bufferPos);
736             bufferPos += compressedDataLength;
737             if (!compresser.finished())
738                 buffer = Arrays.copyOf(buffer, buffer.length + bufferSize);
739         } while (!compresser.finished());
740
741         byte[] result = new byte[bufferPos+4];
742         System.arraycopy(buffer, 0, result, 4, bufferPos);
743         byte sizeData[] = LEInt.toBytes(input.length);
744         System.arraycopy(sizeData, 0, result, 0, 4);
745         return result;
746     }
747
748     /**
749      * Very simple decompression using ZLib.
750      * @param input the compressed input
751      * @return the uncompressed data
752      * @throws DataFormatException
753      */
754     public static byte[] inflate(byte[] input) throws DataFormatException {
755         // Decompress the bytes
756         int inflatedSize = LEInt.toInt(input);
757         Inflater decompresser = new Inflater();
758         decompresser.setInput(input, 4, input.length - 4);
759         byte[] result = new byte[inflatedSize];
760         int resultLength = decompresser.inflate(result);
761         assert(resultLength == inflatedSize);
762         decompresser.end();
763         return result;
764     }
765
766
767     public static boolean isValidFileName(String name) {
768         File f = new File(name) ;
769         if (!f.exists()) {
770             try {
771                 boolean ok = f.createNewFile();
772                 if (ok)
773                     f.delete();
774                 return ok;
775             } catch (IOException ioe) {
776                 return false;
777             }
778         }
779         return true;
780     }
781
782     /**
783      * Create a temporary directory
784      * 
785      * @return temporary directory
786      */
787     public static File createTmpDir()
788     {
789         String tmp = System.getenv("tmp");
790         if (tmp==null) tmp = "c:/temp";
791         Random r = new Random();
792         String randomName = "simantics-tmp-"+(r.nextInt(10000)+10000);
793         File tmpDir = new File(tmp+"/"+randomName);
794         tmpDir.deleteOnExit();
795         Boolean ok = tmpDir.mkdirs();
796         if (!ok) throw new RuntimeException("tmp dir "+tmpDir+" was not created");
797         return tmpDir;
798     }
799     
800     public static void compressZip(String sourcePath, String zipDir) throws IOException {
801         if (LOGGER.isDebugEnabled())
802             LOGGER.debug("Compressing file " + sourcePath + " to zip " + zipDir + ".");
803
804         File filesource = new File(sourcePath);
805         URI base = filesource.toURI();
806         Deque<File> queue = new LinkedList<File>();
807         queue.push(filesource);
808         try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zipDir))) {
809                 while (!queue.isEmpty()) {
810                         filesource = queue.pop();
811                         for (File child : filesource.listFiles()) {
812                                 String name = base.relativize(child.toURI()).getPath();
813                                 if (child.isDirectory()) {
814                                         queue.push(child);
815                                         name = name.endsWith("/") ? name : name + "/";
816                                         zout.putNextEntry(new ZipEntry(name));
817                                 } else {
818                                         zout.putNextEntry(new ZipEntry(name));
819                                         copy(child, zout);
820                                         zout.closeEntry();
821                                 }
822                         }
823                 }
824         } finally {
825                 LOGGER.debug("Filecompression done.");
826         }
827     }
828
829     private static void copy(File file, ZipOutputStream zout) throws IOException {
830         try (InputStream in = new FileInputStream(file)) {
831                 copy(in, zout);
832         }
833     }
834
835         /**
836      * Extract a zip file into a directory
837      * 
838      * @param zipFile
839      * @param dst
840      * @throws IOException
841      */
842     public static void extractZip(File zipFile, File dst) throws IOException {
843         if (LOGGER.isDebugEnabled())
844                 LOGGER.debug("Extracting zip "+zipFile);
845         try (FileInputStream fis = new FileInputStream(zipFile)) {
846             extractZip(fis, dst);
847         }
848     }
849
850     /**
851      * Extract a zip file into a directory
852      * 
853      * @param zipInput
854      * @param dst directory
855      * @throws IOException
856      */
857     public static void extractZip(InputStream zipInput, File dst) throws IOException {
858         byte[] buf = new byte[8192];
859         ZipInputStream zis = new ZipInputStream(zipInput);
860         ZipEntry entry;
861
862         entry = zis.getNextEntry();
863         while (entry != null) {
864             // for each entry to be extracted
865             String name = entry.getName();
866             if (LOGGER.isDebugEnabled())
867                 LOGGER.debug("Extracting "+name);
868             File file = new File(dst, name);
869
870             if (entry.isDirectory())
871             {
872                 if ( !file.exists() ) file.mkdirs();
873             } else {
874                 File parent = file.getParentFile();
875                 if (!parent.exists()) parent.mkdirs();
876                 if (!file.exists()) file.createNewFile();
877
878                 FileOutputStream fileoutputstream = new FileOutputStream(file);
879                 try {
880                     int n = 0;
881                     while ((n = zis.read(buf, 0, buf.length)) > -1)
882                         fileoutputstream.write(buf, 0, n);
883                 } catch (ZipException e) {
884                         throw new IOException("Failed to extract '" + name + "'.", e);
885                 } finally {
886                     fileoutputstream.close();
887                 }
888             }
889
890             zis.closeEntry();
891             entry = zis.getNextEntry();
892         }// while
893
894         zis.close();
895     }
896
897     // Test inflate & deflate
898     public static void main(String[] args) {
899         try {
900             // Test compression
901             String str = "abcdefghijklmnopqrstuvwxyz";
902             byte data[] = str.getBytes();
903             System.out.println("Data:\t"+data.length);
904             byte deflated[] = deflate(data);
905             System.out.println("Deflated:\t"+deflated.length);
906             byte inflated[] = inflate(deflated);
907             System.out.println("Inflated:\t"+inflated.length);
908             String str2 = new String(inflated);
909             System.out.println("Strings are equal: "+str.endsWith(str2));
910         } catch (DataFormatException e) {
911             e.printStackTrace();
912         }
913     }
914
915     public static File[] listFilesByExtension(File directory, final String extension) {
916         return directory.listFiles(new ExtensionFilter(extension));
917     }
918     
919     /**
920      * Copy the content of the input stream into the output stream, using a temporary
921      * byte array buffer whose size is defined by {@link #IO_BUFFER_SIZE}.
922      *
923      * @param in The input stream to copy from.
924      * @param out The output stream to copy to.
925      *
926      * @throws IOException If any error occurs during the copy.
927      */
928     private static final int IO_BUFFER_SIZE = 64 * 1024;
929
930     public static void copy(InputStream in, OutputStream out) throws IOException {
931         byte[] b = new byte[IO_BUFFER_SIZE];
932         int read;
933         while (true) {
934             read = in.read(b);
935             if (read < 0)
936                 break;
937             out.write(b, 0, read);
938         }
939     }
940
941     /**
942      * @param in
943      * @param out
944      * @param maxBytesToCopy the maximum amount of bytes to copy
945      * @return the amount of bytes copied
946      * @throws IOException
947      */
948     public static long copy(InputStream in, OutputStream out, long maxBytesToCopy) throws IOException {
949         byte[] b = new byte[IO_BUFFER_SIZE];
950         int read = 0;
951         while (read < maxBytesToCopy) {
952             int l = (int) Math.min((long) IO_BUFFER_SIZE, maxBytesToCopy-read);
953             int r = in.read(b, 0, l);
954             if (r < 0)
955                 break;
956             out.write(b, 0, r);
957             read += r;
958         }
959         return read;
960     }
961
962     public static void delete(Path databaseLocation) throws IOException {
963         Files.walkFileTree(databaseLocation, new DeleteDirectoriesVisitor());
964     }
965     
966     public static void copy(Path from, Path to) throws IOException {
967         Files.walkFileTree(from, new CopyDirectoriesVisitor(from, to));
968     }
969     
970     public static class DeleteDirectoriesVisitor extends SimpleFileVisitor<Path> {
971         
972         @Override
973         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
974             Files.delete(file);
975             if (Files.exists(file))
976                 throw new IOException("Could not delete file " + file.toAbsolutePath().toString());
977             return FileVisitResult.CONTINUE;
978         }
979         
980         @Override
981         public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
982             if (exc != null)
983                 throw exc;
984             Files.delete(dir);
985             if (Files.exists(dir))
986                 throw new IOException("Could not delete file " + dir.toAbsolutePath().toString());
987             return FileVisitResult.CONTINUE;
988         }
989     }
990     
991     public static class CopyDirectoriesVisitor extends SimpleFileVisitor<Path> {
992         
993         private final Path fromPath;
994         private final Path toPath;
995         
996         public CopyDirectoriesVisitor(Path fromPath, Path toPath) {
997             this.fromPath = fromPath;
998             this.toPath = toPath;
999         }
1000         
1001         @Override
1002         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
1003             Path targetPath = toPath.resolve(fromPath.relativize(dir));
1004             if(!Files.exists(targetPath)){
1005                 Files.createDirectory(targetPath);
1006             }
1007             return FileVisitResult.CONTINUE;
1008         }
1009
1010         @Override
1011         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1012             Files.copy(file, toPath.resolve(fromPath.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
1013             return FileVisitResult.CONTINUE;
1014         }
1015     }
1016     
1017     public static void syncFile(File file) throws IOException {
1018         try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
1019                 raf.getFD().sync();
1020                 }
1021     }
1022 }