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