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