1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.utilities.io;
22 
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.PushbackInputStream;
29 import java.net.URL;
30 import java.net.URLConnection;
31 import java.util.zip.GZIPInputStream;
32 
33 /**
34  * Various static helper methods to deal with files and file names.
35  *
36  * @author Erich Schubert
37  * @since 0.2
38  */
39 public final class FileUtil {
40   /**
41    * Fake Constructor. Use static methods.
42    *
43    */
FileUtil()44   private FileUtil() {
45     // Do not instantiate.
46   }
47 
48   /**
49    * Returns the lower case extension of the selected file.
50    *
51    * If no file is selected, <code>null</code> is returned.
52    *
53    * @param file File object
54    * @return Returns the extension of the selected file in lower case or
55    *         <code>null</code>
56    */
getFilenameExtension(File file)57   public static String getFilenameExtension(File file) {
58     return getFilenameExtension(file.getName());
59   }
60 
61   /**
62    * Returns the lower case extension of the selected file.
63    *
64    * If no file is selected, <code>null</code> is returned.
65    *
66    * @param name File name
67    * @return Returns the extension of the selected file in lower case or
68    *         <code>null</code>
69    */
getFilenameExtension(String name)70   public static String getFilenameExtension(String name) {
71     if(name == null) {
72       return null;
73     }
74     int index = name.lastIndexOf('.');
75     return index < 0 ? null : name.substring(index + 1).toLowerCase();
76   }
77 
78   /**
79    * Try to open a file, first trying the file system, then falling back to the
80    * classpath.
81    *
82    * @param filename File name in system notation
83    * @return Input stream
84    * @throws FileNotFoundException When no file was found.
85    */
openSystemFile(String filename)86   public static InputStream openSystemFile(String filename) throws FileNotFoundException {
87     try {
88       return new FileInputStream(filename);
89     }
90     catch(FileNotFoundException e) {
91       // try with classloader
92       String resname = File.separatorChar != '/' ? filename.replace(File.separatorChar, '/') : filename;
93       ClassLoader cl = FileUtil.class.getClassLoader();
94       InputStream result = cl.getResourceAsStream(resname);
95       if(result != null) {
96         return result;
97       }
98       // Sometimes, URLClassLoader does not work right. Try harder:
99       URL u = cl.getResource(resname);
100       if(u == null) {
101         throw e;
102       }
103       try {
104         URLConnection conn = u.openConnection();
105         conn.setUseCaches(false);
106         if((result = conn.getInputStream()) != null) {
107           return result;
108         }
109       }
110       catch(IOException x) {
111         throw e; // Throw original error instead.
112       }
113       throw e;
114     }
115   }
116 
117   /**
118    * Try to open a stream as gzip, if it starts with the gzip magic.
119    *
120    * @param in original input stream
121    * @return old input stream or a {@link GZIPInputStream} if appropriate.
122    * @throws IOException on IO error
123    */
tryGzipInput(InputStream in)124   public static InputStream tryGzipInput(InputStream in) throws IOException {
125     // try autodetecting gzip compression.
126     if(!in.markSupported()) {
127       PushbackInputStream pb = new PushbackInputStream(in, 16);
128       // read a magic from the file header, and push it back
129       byte[] magic = { 0, 0 };
130       int r = pb.read(magic);
131       pb.unread(magic, 0, r);
132       return (magic[0] == 31 && magic[1] == -117) ? new GZIPInputStream(pb) : pb;
133     }
134     // Mark is supported.
135     in.mark(16);
136     boolean isgzip = ((in.read() << 8) | in.read()) == GZIPInputStream.GZIP_MAGIC;
137     in.reset(); // Rewind
138     return isgzip ? new GZIPInputStream(in) : in;
139   }
140 
141   /**
142    * Try to locate an file in the filesystem, given a partial name and a prefix.
143    *
144    * @param name file name
145    * @param basedir extra base directory to try
146    * @return file, if the file could be found. {@code null} otherwise
147    */
locateFile(String name, String basedir)148   public static File locateFile(String name, String basedir) {
149     // Try exact match first.
150     File f = new File(name);
151     if(f.exists()) {
152       return f;
153     }
154     // Try with base directory
155     if(basedir != null) {
156       if((f = new File(basedir, name)).exists()) {
157         return f;
158       }
159     }
160     // try stripping whitespace
161     String name2;
162     if(!name.equals(name2 = name.trim())) {
163       if((f = locateFile(name2, basedir)) != null) {
164         return f;
165       }
166     }
167     // try substituting path separators
168     if(!name.equals(name2 = name.replace('/', File.separatorChar))) {
169       if((f = locateFile(name2, basedir)) != null) {
170         return f;
171       }
172     }
173     if(!name.equals(name2 = name.replace('\\', File.separatorChar))) {
174       if((f = locateFile(name2, basedir)) != null) {
175         return f;
176       }
177     }
178     // try stripping extra characters, such as quotes.
179     if(name.length() > 2 && name.charAt(0) == '"' && name.charAt(name.length() - 1) == '"') {
180       if((f = locateFile(name.substring(1, name.length() - 1), basedir)) != null) {
181         return f;
182       }
183     }
184     return null;
185   }
186 
187   /**
188    * Load an input stream (e.g., a Java resource) into a String buffer. The
189    * stream is closed afterwards.
190    *
191    * @param is Input stream
192    * @return String with file/resource contents.
193    * @throws IOException on IO errors
194    */
slurp(InputStream is)195   public static String slurp(InputStream is) throws IOException {
196     StringBuilder buf = new StringBuilder();
197     final byte[] b = new byte[4096];
198     for(int n; (n = is.read(b)) != -1;) {
199       buf.append(new String(b, 0, n));
200     }
201     is.close();
202     return buf.toString();
203   }
204 }
205