package bluej.utility;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import bluej.pkgmgr.Package;
import bluej.pkgmgr.Project;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.stage.Window;

import threadchecker.OnThread;
import threadchecker.Tag;
import bluej.Config;
import bluej.extensions.SourceType;
import bluej.prefmgr.PrefMgr;


| A file utility for various file related actions. | | @author Markus Ostman | @author Michael Kolling | @OnThread(Tag.Swing) public class FileUtility {
| Enum used to indicate file write capabilities on Windows. | @author polle | @see FileUtility#getVistaWriteCapabilities(File) | public enum WriteCapabilities {READ_ONLY, NORMAL_WRITE, VIRTUALIZED_WRITE, UNKNOWN }
| Gets a directory containing a project to open. | | First, the user is shown a directory chooser. If this is a BlueJ/Greenfoot *package*, | we navigate up the hierarchy to find the uppermost (parent-most?) directory with | a package, i.e. the surrounding project. (This effectively was done using the Swing chooser, because it was | impossible to navigate into a project to choose a sub-package, and attempting | to paste the path of a sub-package would navigate upwards to find the project.) We | don't want to let the user select sub-packages, anyway. | | If the user selects a directory which is not a BlueJ/Greenfoot package, we present | a dialog telling them so, and invite them to choose again or cancel. As an additional | help, if any subdirectories of the chosen item are packages, we offer a handful of them | as options in this dialog. | | @param parent The parent window for the file chooser and dialog | @return A chosen File with a BlueJ/Greenfoot project, or null if the user cancelled. | @OnThread(Tag.FXPlatform) public static File getOpenProjectFX(Window parent) { File originalDir = getOpenDirFX(parent, Config.getString("pkgmgr.openPkg.title"), true); if (originalDir == null) return null; File dir = originalDir; while (dir != null && dir.getParentFile() != null && Package.isPackage(dir.getParentFile())) { dir = dir.getParentFile(); } if (!Package.isPackage(dir)) { List<File> subDirs = null; if (dir != null) { subDirs = Arrays.asList(dir.listFiles(f -> f.isDirectory() && Package.isPackage(f))); } NotAProjectDialog dlg = new NotAProjectDialog(parent, originalDir, subDirs); dlg.showAndWait(); if (dlg.isCancel()) return null; else if (dlg.isChooseAgain()) return getOpenProjectFX(parent); else{ return dlg.getSelectedDir(); } } else { return dir; } } @OnThread(Tag.FXPlatform) public static File getSaveProjectFX(Project project, Window parent, String title) { File chosen = new ProjectLocationDialog(project, parent, title).showAndWait(); if (chosen == null) return null; if (chosen != null && chosen.getParentFile() != null) { PrefMgr.setProjectDirectory(chosen.getParentFile().getPath()); } return chosen; } @OnThread(Tag.FXPlatform) public static List getMultipleFilesFX(Window parent, String title, ExtensionFilter filter) { FileChooser chooser = new FileChooser(); chooser.setInitialDirectory(PrefMgr.getProjectDirectory()); if (filter != null) chooser.getExtensionFilters().setAll(filter); chooser.setTitle(title); return chooser.showOpenMultipleDialog(parent); } @OnThread(Tag.FXPlatform) public static List getOpenFilesFX(Window parent, String title, List<FileChooser.ExtensionFilter> filters, boolean rememberDir) { FileChooser newChooser = new FileChooser(); newChooser.getExtensionFilters().setAll(filters); newChooser.setTitle(title); newChooser.setInitialDirectory(PrefMgr.getProjectDirectory()); List<File> chosen = newChooser.showOpenMultipleDialog(parent); if (chosen != null && chosen.size() > 0 && chosen.get(0).getParentFile() != null && rememberDir) { PrefMgr.setProjectDirectory(chosen.get(0).getParentFile().getPath()); } return chosen; } @OnThread(Tag.FXPlatform) public static File getSaveFileFX(Window parent, String title, List<FileChooser.ExtensionFilter> filters, boolean rememberDir) { FileChooser newChooser = new FileChooser(); if (filters != null) newChooser.getExtensionFilters().setAll(filters); newChooser.setTitle(title); newChooser.setInitialDirectory(PrefMgr.getProjectDirectory()); File chosen = newChooser.showSaveDialog(parent); if (chosen != null && chosen.getParentFile() != null && rememberDir) { PrefMgr.setProjectDirectory(chosen.getParentFile().getPath()); } return chosen; } @OnThread(Tag.FXPlatform) public static File getOpenArchiveFX(Window parent, String title, boolean rememberDir) { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().setAll(new ExtensionFilter("ZIP/JAR file", "*.zip", "*.jar")); if (title != null) chooser.setTitle(title); chooser.setInitialDirectory(PrefMgr.getProjectDirectory()); File chosen = chooser.showOpenDialog(parent); if (chosen != null && chosen.getParentFile() != null && rememberDir) { PrefMgr.setProjectDirectory(chosen.getParentFile().getPath()); } return chosen; } @OnThread(Tag.FXPlatform) public static File getOpenDirFX(Window parent, String title, boolean rememberDir) { DirectoryChooser newChooser = new DirectoryChooser(); newChooser.setTitle(title); if (PrefMgr.getProjectDirectory() != null) newChooser.setInitialDirectory(PrefMgr.getProjectDirectory()); File chosen = newChooser.showDialog(parent); if (chosen != null && chosen.getParentFile() != null && rememberDir) { PrefMgr.setProjectDirectory(chosen.getParentFile().getPath()); } return chosen; } @OnThread(Tag.FX) public static ExtensionFilter getJavaStrideSourceFilterFX() { return new ExtensionFilter("Java/Stride source", "*." + SourceType.Java.getExtension(), "*." + SourceType.Stride.getExtension()); }
| Copy file 'source' to file 'dest'. The source file must exist, | the destination file will be created. Returns true if successful. | @OnThread(Tag.Any) public static void copyFile(String source, String dest) throws IOException { File srcFile = new File(source); File destFile = new File(dest); copyFile(srcFile, destFile); }
| Copy file 'srcFile' to file 'destFile'. The source file must exist, | the destination file will be created. Returns true if successful. | @OnThread(Tag.Any) public static void copyFile(File srcFile, File destFile) throws IOException { if (srcFile.equals(destFile)) { return; } InputStream in = null; OutputStream out = null; try { in = new BufferedInputStream(new FileInputStream(srcFile)); out = new BufferedOutputStream(new FileOutputStream(destFile)); copyStream(in, out); } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } }
| Copy stream 'in' to stream 'out'. | @OnThread(Tag.Any) public static void copyStream(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[4096]; for (int c; (c = in.read(buffer)) != -1; ) out.write(buffer, 0, c); }
| Copy (recursively) a whole directory. | public static final int NO_ERROR = 0; public static final int SRC_NOT_DIRECTORY = 2; public static final int COPY_ERROR = 3; public static final int DEST_EXISTS_NOT_DIR = 4; public static final int DEST_EXISTS_NON_EMPTY = 5; @OnThread(Tag.Any) public static int copyDirectory(File srcFile, File destFile) { if (!srcFile.isDirectory()) return SRC_NOT_DIRECTORY; if (destFile.exists() && !destFile.isDirectory()) return DEST_EXISTS_NOT_DIR; if (destFile.exists()) { if (destFile.list().length > 0) return DEST_EXISTS_NON_EMPTY; } else { if (!destFile.mkdir()) return COPY_ERROR; } String[] dir = srcFile.list(); for (int i=0; i<dir.length; i++) { File file = new File(srcFile, dir[i]); if (file.isDirectory()) { if (copyDirectory(file, new File(destFile, dir[i])) != NO_ERROR) return COPY_ERROR; } else { File file2 = new File(destFile, dir[i]); try { copyFile(file, file2); } catch (IOException ioe) { return COPY_ERROR; } } } return NO_ERROR; }
| Recursively copy all files from one directory to another. | If destination is a sub directory of source directory then | it returns without copying any files. | | @return An array contained each source file which was | not successfully copied or null if everything went well | @OnThread(Tag.Any) public static File[] recursiveCopyFile(File srcDir, File destDir) { if (srcDir == null || destDir == null) throw new IllegalArgumentException(); File parentDir = destDir.getParentFile(); while (parentDir != null) { if (parentDir.equals(srcDir)) return new File[] { srcDir }; parentDir = parentDir.getParentFile(); } return actualRecursiveCopyFile(srcDir, destDir); } @OnThread(Tag.Any) private static File[] actualRecursiveCopyFile(File srcDir, File destDir) { List<File> failed = new ArrayList<File>(); if (srcDir.getAbsolutePath().equals(destDir.getAbsolutePath())) return null; if (!srcDir.isDirectory() || !destDir.isDirectory()) throw new IllegalArgumentException(); File[] files = srcDir.listFiles(); for (int i=0; i < files.length; i++) { if (files[i].isDirectory()) { File newDir = new File(destDir, files[i].getName()); newDir.mkdir(); if (newDir.isDirectory()) { actualRecursiveCopyFile(files[i], newDir); } else { failed.add(files[i]); } } else if (files[i].isFile()) { File newFile = new File(destDir, files[i].getName()); if (! newFile.exists()) { try { copyFile(files[i], newFile); } catch (IOException ioe) { failed.add(files[i]); } } else { failed.add(files[i]); } } } if (failed.size() > 0) { return failed.toArray(new File[0]); } return null; }
| Find a file with a given extension in a given directory or any | subdirectory. Returns just one randomly selected file with that | extension. | | @return a file with the given extension in the given directory, | or 'null' if such a file cannot be found. | @OnThread(Tag.Any) public static File findFile(File startDir, String suffix) { File[] files = startDir.listFiles(); for (int i=0; i < files.length; i++) { if (files[i].isFile()) { if (files[i].getName().endsWith(suffix)) return files[i]; } } for (int i=0; i < files.length; i++) { if (files[i].isDirectory()) { File found = findFile(files[i], suffix); if (found != null) return found; } } return null; }
| Check whether a given directory contains a file with a given suffix. | The search is NOT recursive. | | @return true if a file with the given suffix exists in the given | directory. | @OnThread(Tag.Any) public static boolean containsFile(File dir, String suffix) { if (dir == null) throw new IllegalArgumentException(); File[] files = dir.listFiles(); if (files == null) throw new IllegalArgumentException(); for (int i=0; i < files.length; i++) { if (files[i].isFile() && files[i].getName().endsWith(suffix)) return true; } return false; }
| Delete a directory recursively. | This method will delete all files and subdirectories in any | directory without asking questions. Use with care. | | @param directory The directory that will be deleted. | @OnThread(Tag.Any) public static void deleteDir(File directory) { File[] fileList = directory.listFiles(); if (fileList == null || Array.getLength(fileList) == 0){ try{ directory.delete(); }catch (SecurityException se){ Debug.message("Trouble deleting: "+directory+se); } } else{ for (int i=0;i<Array.getLength(fileList);i++){ deleteDir(fileList[i]); } try{ directory.delete(); }catch (SecurityException se){ Debug.message("Trouble deleting: "+directory+se); } } }
| Find the relative path from some parent directory to a file nested within. | For instance, for parent "/a/b" and file "/a/b/c/d/somefile.java" returns |* "c/d/somefile.java". * * @param parent The containing directory * @param file The file to get the relative path to * @return The relative path between parent and file */ @OnThread(Tag.Any) public static String makeRelativePath(File parent, File file) | |{ | |String filePath = file.getAbsolutePath(); | |String parentPath = parent.getAbsolutePath(); | |if (filePath.startsWith(parentPath)) { | |// Strip parent path and path separator | |filePath = filePath.substring(parentPath.length() + 1); | |} | |return filePath; | |} | |/** | Get the file write capabilities of the given directory. | To find the capabilities, this method will try creating a | temporary file in the directory. | See trac tickets 147 and 150 for more details. | | @param dir | Directory to check. | @return The capabilities of this directory. Will return | {}link WriteCapabilities#UNKNOWN} if the file is not an existing | directory. | @OnThread(Tag.Any) public static WriteCapabilities getVistaWriteCapabilities(File dir) { if (!dir.isDirectory()) { return WriteCapabilities.UNKNOWN; } WriteCapabilities capabilities = WriteCapabilities.UNKNOWN; File tmpFile = null; try { tmpFile = File.createTempFile("bluej", null, dir); tmpFile.deleteOnExit(); if (isVirtualized(tmpFile)) { capabilities = WriteCapabilities.VIRTUALIZED_WRITE; } else { capabilities = WriteCapabilities.NORMAL_WRITE; } } catch (IOException e) { capabilities = WriteCapabilities.READ_ONLY; } finally { if (tmpFile != null) { tmpFile.delete(); } } return capabilities; }
| Check whether the given file is virtualized by Windows (Vista). | @OnThread(Tag.Any) private static boolean isVirtualized(File file) { boolean isVirtualized = false; if (Config.isModernWinOS()) { try { String canonicalPath = file.getCanonicalPath(); int colonIndex = canonicalPath.indexOf(":"); if (colonIndex > 0) { String pathPart = canonicalPath.substring(colonIndex + 1); String virtualStore = System.getenv("localappdata") + File.separator + "VirtualStore"; String virtualTmpFilePath = virtualStore + pathPart; isVirtualized = new File(virtualTmpFilePath).exists(); } } catch (IOException e) { Debug.reportError( "Error when testing for Windows virtualisation.", e); } } return isVirtualized; } }
top, use, map, class FileUtility

.   getOpenProjectFX
.   getSaveProjectFX
.   getMultipleFilesFX
.   getOpenFilesFX
.   getSaveFileFX
.   getOpenArchiveFX
.   getOpenDirFX
.   getJavaStrideSourceFilterFX
.   copyFile
.   copyFile
.   copyStream
.   copyDirectory
.   recursiveCopyFile
.   actualRecursiveCopyFile
.   findFile
.   containsFile
.   deleteDir
.   getVistaWriteCapabilities
.   isVirtualized




615 neLoCode + 65 LoComm