001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009 010package imagingbook.core; 011 012import javax.swing.JFileChooser; 013import javax.swing.LookAndFeel; 014import javax.swing.UIManager; 015import java.io.File; 016import java.io.FileOutputStream; 017import java.io.IOException; 018import java.io.InputStream; 019import java.net.URI; 020import java.net.URISyntaxException; 021import java.net.URL; 022import java.net.URLClassLoader; 023import java.nio.file.Path; 024import java.nio.file.Paths; 025import java.util.jar.Manifest; 026 027 028/** 029 * This class defines various static methods for managing 030 * file-based resources and JAR manifest files. 031 * 032 * @author WB 033 */ 034public abstract class FileUtils { 035 036 /** 037 * Removes the extension part of a pathname. 038 * Examples:<br> 039 * "foo.txt" → "foo", 040 * "foo" → "foo", 041 * "foo." → "foo.", 042 * ".txt" → ".txt". 043 * @param name the pathname 044 * @return the pathname without the extension (if valid) 045 */ 046 public static String stripFileExtension(String name) { 047 int dotInd = name.lastIndexOf('.'); 048 // if dot is in the first position, 049 // we are dealing with a hidden file rather than an DefaultFileExtension 050 return (dotInd > 0) ? name.substring(0, dotInd) : name; 051 } 052 053 /** 054 * Extracts the extension part of a pathname as a string. 055 * Examples:<br> 056 * "foo.txt" → "txt", 057 * "foo" → "", 058 * "foo." → "", 059 * ".txt" → "". 060 * @param name the pathname 061 * @return the extension or an empty string 062 */ 063 public static String getFileExtension(String name) { 064 int dotInd = name.lastIndexOf('.'); 065 return (dotInd > 0) ? name.substring(dotInd + 1) : ""; 066 } 067 068 // ---- resources-related stuff ---------------------------------- 069 070 /** 071 * Find the path from which a given class was loaded. 072 * @param clazz a class. 073 * @return the path of the .class file for the given class or null (e.g. 074 * if the class is anonymous). 075 */ 076 public static String getClassPath(Class<?> clazz) { 077 String path = null; 078 try { 079 URI uri = clazz.getProtectionDomain().getCodeSource().getLocation().toURI(); 080 if (uri != null && !uri.getPath().isEmpty()) { 081 path = new File(uri).getCanonicalPath(); 082 } 083 } catch (URISyntaxException | IOException e) { } 084 return path; 085 } 086 087// public static String getClassPath(Class<?> clazz) { 088// return clazz.getProtectionDomain().getCodeSource().getLocation().getFile(); 089// //return clazz.getProtectionDomain().getCodeSource().getLocation().toString(); 090// } 091 092 // ---------------------------------------------------------------- 093 094 /** 095 * Lists (to System.out) the paths where classes are loaded from. 096 */ 097 public static void printClassPath() { 098 ClassLoader cl = ClassLoader.getSystemClassLoader(); 099 URL[] urls = ((URLClassLoader) cl).getURLs(); 100 for (URL url : urls) { 101 System.out.println(url.getPath()); 102 } 103 } 104 105 // ---------------------------------------------------------------- 106 107 /** 108 * Checks 'by name' if a particular class exists. 109 * 110 * @param classname fully qualified name of the class, e.g. {@literal imagingbook.lib.util.FileUtils} 111 * @return {@code true} if the class was found, {@code false} otherwise 112 */ 113 public static boolean checkClass(String classname) { 114 // String logStr = " checking class " + classname + " ... "; 115 try { 116 if (Class.forName(classname) != null) { 117 // IJ.log(logStr + "OK"); 118 return true; 119 } 120 } catch (ClassNotFoundException e) { } 121 // IJ.log(logStr + "ERROR"); 122 return false; 123 } 124 125 126 // ---------------------------------------------------------------- 127 128 // from https://bukkit.org/threads/extracting-file-from-jar.16962/ 129 /** 130 * Reads all data from the given input stream and copies them 131 * to to a file. 132 * @param in the input stream 133 * @param file the output file 134 * @throws IOException if anything goes wrong 135 */ 136 public static void copyToFile(InputStream in, File file) throws IOException { 137 FileOutputStream out = new FileOutputStream(file); 138 try { 139 byte[] buf = new byte[1024]; 140 int i = 0; 141 while ((i = in.read(buf)) != -1) { 142 out.write(buf, 0, i); 143 } 144 } catch (IOException e) { 145 throw e; 146 } finally { 147 if (in != null) { 148 in.close(); 149 } 150 if (out != null) { 151 out.close(); 152 } 153 } 154 } 155 156 157 158 // ---------------------------------------------------------------- 159 160 /** 161 * Finds the manifest (from META-INF/MANIFEST.MF) of the JAR file 162 * from which {@literal clazz} was loaded. 163 * 164 * See: http://stackoverflow.com/a/1273432 165 * @param clazz A class in the JAR file of interest. 166 * @return A {@link Manifest} object or {@literal null} if {@literal clazz} 167 * was not loaded from a JAR file. 168 */ 169 public static Manifest getJarManifest(Class<?> clazz) { 170 String className = clazz.getSimpleName() + ".class"; 171 String classPath = clazz.getResource(className).toString(); 172 //IJ.log("classPath = " + classPath); 173 if (!classPath.startsWith("jar")) { // Class not from JAR 174 return null; 175 } 176 String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; 177 Manifest manifest = null; 178 try { 179 manifest = new Manifest(new URL(manifestPath).openStream()); 180 } catch (IOException ignore) { } 181 return manifest; 182 } 183 184 // ---------------------------------------------------------------- 185 186 187 /** 188 * Opens a dialog for the user to select a single folder (no files). 189 * Contained files and sub-folders are shown. 190 * Uses native (system) look-and-feel; original look-and-feel is restored. 191 * 192 * @param startDirectory the directory to start from (pass {@code ""} or {@code "."} for the current directory) 193 * @param dialogTitle the string shown in the title bar of the dialog window 194 * @return a {@link File} object representing the selected directory or {@code null} if the dialog was canceled 195 */ 196 public static File selectFolder(String startDirectory, String dialogTitle) { 197 File startDir = new File(startDirectory); 198 if (!startDir.exists()) { 199 startDir = new File("."); 200 } 201 202 JFileChooser chooser = null; 203 LookAndFeel laf = UIManager.getLookAndFeel(); // get current look-and-feel 204 205 try { 206 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // use native look and feel 207 chooser = new JFileChooser(startDir) { 208 private static final long serialVersionUID = 1L; 209 public void approveSelection() { 210 if (getSelectedFile().isFile()) { 211 return; 212 } else { 213 super.approveSelection(); 214 } 215 } 216 }; 217 UIManager.setLookAndFeel(laf); // reset look-and-feel 218 } 219 catch (Exception e) { } 220 221 if (chooser == null) 222 return null; 223 224 chooser.setDialogTitle(dialogTitle); 225 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 226 chooser.setAcceptAllFileFilterUsed(true); 227 chooser.setMultiSelectionEnabled(false); 228 chooser.setSelectedFile(new File(".")); // see https://stackoverflow.com/a/48316113 229 230 if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { 231 File selected = chooser.getSelectedFile(); 232 if (selected == null) 233 throw new RuntimeException("selected directory is null"); 234 return selected; 235 } 236 else { // dialog was canceled 237 return null; 238 } 239 } 240 241 // ----------------------------------------------------------------- 242 243 private static final String DirectoryKey = "imagingbook#dir"; 244 private static String DefaultUserDirectory = System.getProperty("user.dir"); 245 246 /** 247 * Sets a system property to memorize the current directory. 248 * Use {@link #getCurrentDirectory()} to retrieve this value. 249 * If the specified pathname is not a directory (i.e., a plain file), 250 * its parent directory is used. 251 * 252 * @param pathname a directory of file path 253 */ 254 @Deprecated // use setCurrentDirectory(Class, String) instead 255 public static void setCurrentDirectory(String pathname) { 256 File file = new File(pathname); 257 String dir = (file.isDirectory()) ? file.toString() : file.getParent(); 258 System.setProperty(DirectoryKey, dir); 259 } 260 261 /** 262 * Associates a current directory with the specified class by 263 * setting a system property to make this persistent through class reloads. 264 * Use {@link #getCurrentDirectory(Class)} to retrieve this value. 265 * If the specified pathname is not a directory (i.e., a plain file), 266 * its parent directory is used. 267 * 268 * @param clazz the class to associate the directory with 269 * @param pathname a directory of file path 270 */ 271 public static void setCurrentDirectory(Class<?> clazz, String pathname) { 272 File file = new File(pathname); 273 String dir = (file.isDirectory()) ? file.toString() : file.getParent(); 274 System.setProperty(makeDirectoryKey(clazz), dir); 275 } 276 277 public static void setCurrentDirectory(Class<?> clazz, Path path) { 278 File file = path.toFile(); 279 String dir = (file.isDirectory()) ? file.toString() : file.getParent(); 280 System.setProperty(makeDirectoryKey(clazz), dir); 281 } 282 283 /** 284 * Returns the current directory path. 285 * If the directory was set with {@link #setCurrentDirectory(String)} before 286 * this path is returned, 287 * otherwise the value for the "user.dir" system property. 288 * 289 * @return a string with the current directory path 290 */ 291 @Deprecated // use getCurrentDirectory(Class) instead 292 public static String getCurrentDirectory() { 293 String dir = System.getProperty(DirectoryKey); 294 return (dir != null) ? dir : DefaultUserDirectory; 295 } 296 297 298 /** 299 * Returns the current directory associated with the specified class. 300 * (usually of type {@code PlugIn} or {@code PlugInFilter}). 301 * If the directory was set with {@link #setCurrentDirectory(Class,String)} before 302 * this path is returned, {@code null} otherwise. 303 * 304 * @param clazz the class to associate the directory with 305 * @return a string with the current directory path or null if not registered 306 */ 307 public static String getCurrentDirectory(Class<?> clazz) { 308 return System.getProperty(makeDirectoryKey(clazz)); 309 } 310 311 private static String makeDirectoryKey(Class<?> clazz) { 312 return DirectoryKey + "#" + clazz.getCanonicalName(); 313 } 314 315 316 public static String getTempDirectory() { 317 String tempDir = System.getProperty("java.io.tmpdir"); 318 if (tempDir == null) { 319 throw new RuntimeException("temporary user directory is undefined"); 320 } 321 return tempDir; 322 } 323 324 public static String getModuleName(Class<?> clazz) { 325 326 return null; 327 } 328 329 public static String getModuleName2(Class<?> clazz) throws URISyntaxException { 330 System.out.println("getResource = " + clazz.getResource("")); 331 System.out.println("clsName = " + clazz.getCanonicalName()); 332 System.out.println("pkgName = " + clazz.getPackage().getName()); 333 334 Path ppp = Paths.get("imagingbook-public"); 335 336 String path = null; 337 try { 338 URI uri = clazz.getProtectionDomain().getCodeSource().getLocation().toURI(); 339 if (uri != null && !uri.getPath().isEmpty()) { 340 // path = new File(uri).getPath(); 341 // path = new File(uri).getCanonicalPath(); 342 Path p = Paths.get(uri); 343 System.out.println("p = " + p); 344 int n = p.getNameCount(); 345 System.out.println("name count = " + n); 346 int k = -1; 347 for (int i = 0; i < n; i++) { 348 System.out.println(" " + p.getName(i)); 349 if (p.getName(i).equals(ppp)) { 350 k = i + 1; 351 break; 352 } 353 } 354 355 if (k >= 0 && k < n-1) 356 return p.getName(k).toString(); 357 } 358 } catch (URISyntaxException e) { } 359 return null; 360 } 361 362 363 364 365 // ----------------------------------------------------------------- 366 367 public static void main(String[] args) throws URISyntaxException { 368 369 370 Class<?> clazz = FileUtils.class; 371 String specs = clazz.getPackage().getSpecificationTitle(); 372 System.out.println("specs = " + specs); 373 374 System.out.println("module name = " + getModuleName2(FileUtils.class)); 375 376 } 377 378 379}