package randomfilecopy; import java.nio.file.*; import java.util.*; import java.io.IOException; import javax.swing.*; /** Handles the actual execution of the program's copying features */ public class CopyEngine extends SwingWorker<Void,Integer> { public CopySettings settings; private Path srcDirPath = null; // path of source directory private Path destDirPath = null; // path of destination directory private int numSrcFiles = 0; // total number of valid files in source directories private long totalSize; // total size in bytes of all files copied private int totalFiles; // total number of files copied private String[] filter; // array of file extensions used by file filter private EventLogger log = null; // log being used by the main window private CopyEngine() { } public CopyEngine(EventLogger log) { super(); settings = new CopySettings(); this.log = log; } @Override protected void doInBackground() { srcDirPath = Paths.get( settings.src ); destDirPath = Paths.get( settings.dest ); log.write( "Execution begun..."); log.write( "Source directory = " + settings.src ); log.write( "Destination directory = " + settings.dest ); // create file extension filters filter = settings.filters.split( "[ .,]+" ); // dirFiles is a list of lists, the inner lists are all the files in a directory, the outer list is of all directories List<List> dirFiles = new ArrayList<List>(); createMasterFileList(srcDirPath, dirFiles); log.write( "Number of directories searched = " + dirFiles.size() ); if (isCancelled() == true) { firePropertyChange("status", null, "Cancelled"); return null; } copyFilesToDest( createRandomList(dirFiles) ); return null; } @Override protected void done() { String status = "Done : Copied " + totalFiles + " files, total size " + String.format("%.2f", totalSize / 1048576.0) + "(MB)"; firePropertyChange("done", null, status); } /** Creates a list of all files in the sent directory, adds it to the master list, then calls itself on all subdirectories */ private void createMasterFileList(Path currentDir, List<List> masterList) { // user wants to cancel? if (isCancelled() == true) return; DirectoryStream<Path> stream = null; List<Path> currDirFiles = null; int numFiles = 0; try { // iterate over all subdirectories stream = Files.newDirectoryStream(currentDir); for (Path currentPath: stream) { if ( Files.isDirectory( currentPath ) ) createMasterFileList(currentPath, masterList); else numFiles++; } stream.close(); firePropertyChange("status", null, "Scanning : " + currentDir.toString()); // iterate over all files in current directory currDirFiles = new ArrayList<Path>(numFiles); stream = Files.newDirectoryStream(currentDir); for (Path currentPath: stream) if ( !Files.isDirectory( currentPath ) ) currDirFiles.add( currentPath ); stream.close(); } catch (NoSuchFileException e) { JOptionPane.showMessageDialog( null, "Source directory does not exist.", "Error", JOptionPane.WARNING_MESSAGE ); } catch (Exception e) { System.err.println("Error searching source directory : " + e); } finally { try{ stream.close(); } // close the stream catch(IOException e) { System.err.println("Error searching source directory : " + e ); } } // if file list for this directory is not empty, add it to master file list if (!currDirFiles.isEmpty()) { masterList.add( currDirFiles ); numSrcFiles += currDirFiles.size(); } } // end createMasterFileList() /** Randomly selects files from sent list in accordance with our parameters and returns list of chosen files */ private List createRandomList(List<List> dirFiles) { List<Path> copyList = new ArrayList<Path>(settings.maxFiles); totalSize = 0; totalFiles = 0; int dirIndex, fileIndex; long fileSize; Path tempPath; firePropertyChange("status", null, "Picking Random Files..."); while (totalSize < settings.maxTotalSize && totalFiles < settings.maxFiles && numSrcFiles > 0) { if (!settings.normalize) { dirIndex = 0; fileIndex = (int)(Math.random() * numSrcFiles); while ( fileIndex >= dirFiles.get(dirIndex).size() ) fileIndex -= dirFiles.get(dirIndex++).size(); } else { // pick a random directory and random file in that directory do { dirIndex = (int)(Math.random() * dirFiles.size() ); } while ( dirFiles.get(dirIndex).isEmpty() ); // if directory has been emptied of files, find a new one fileIndex = (int)(Math.random() * dirFiles.get(dirIndex).size()); } tempPath = (Path)dirFiles.get(dirIndex).get( fileIndex ); fileSize = getFileSizeInBytes( tempPath ); if ( isValidFile(tempPath, fileSize) ) { copyList.add( tempPath ); totalSize += fileSize; totalFiles++; } // remove file from list, either we used it, or it's invalid for some reason dirFiles.get(dirIndex).remove( fileIndex ); numSrcFiles--; } log.write("Number of files selected to copy = " + copyList.size() ); return copyList; } // end createRandomList() private boolean isValidFile(Path file, long fileSize) { if (fileSize + totalSize < settings.maxTotalSize && fileSize < settings.maxFileSize && fileSize > settings.minFileSize) { if (settings.useFilter) { // get file extension String name = file.getFileName().toString(); String ext = name.substring( name.lastIndexOf('.') + 1); //check to see if extension matches a file filter boolean matchedFilter = false; for (int i = 0; i < filter.length; i++) if (ext.equalsIgnoreCase( filter[i]) ) matchedFilter = true; // file is invalid if user wants only certain extensions, and we have no match // or if user does not want certain extensions, and we do have a match if ( (settings.filterInclude && matchedFilter == false) || (!settings.filterInclude && matchedFilter == true) ) return false; } return true; } return false; } /** Copies all files in the sent list into the destination directory */ private void copyFilesToDest(List<Path> copyList) { Path srcFilePath, destFilePath; if (copyList.isEmpty() ) { JOptionPane.showMessageDialog( null, "No valid files found.", "Error", JOptionPane.WARNING_MESSAGE ); return; } // create new destination directory if needed, clear if needed try { if ( !Files.exists( destDirPath ) ) Files.createDirectories( destDirPath ); else if (settings.clearDestDir) { Object o = "Are you sure you want to delete all files in the destination directory?"; int result = JOptionPane.showConfirmDialog( null, o, "Delete All Files", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (result == JOptionPane.YES_OPTION) deleteFilesInDir( destDirPath ); } } catch (IOException e) { System.err.println(e); } // keep track of how many files/bytes have been copied totalSize = 0; totalFiles = 0; // copy files to destination directory for (int i = 0; i < copyList.size(); i++) { if (isCancelled() == true) return; // user wants to cancel? srcFilePath = copyList.get( i ); destFilePath = destDirPath.resolve( srcFilePath.getFileName() ); firePropertyChange("status", null, "Copying file : " + srcFilePath.getFileName()); try { Files.copy( srcFilePath, destFilePath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { System.err.println(e + "\nsrcPath = " + srcFilePath.toString() ); System.err.println( "destPath = " + destFilePath.toString() ); } totalFiles++; totalSize += getFileSizeInBytes(srcFilePath); setProgress( (int)( ((double)(i+1) / copyList.size()) * 100.0 ) ); } } // end copyFilesToDest() /** Deletes all files in the sent directory, leaves sub-directories alone */ private void deleteFilesInDir(Path dir) { DirectoryStream<Path> stream = null; firePropertyChange("status", null, "Clearing Destination Directory..."); try { stream = Files.newDirectoryStream(dir); for (Path currentPath: stream) if ( !Files.isDirectory( currentPath ) ) // dont attempt to delete directories Files.delete( currentPath ); stream.close(); } catch (IOException e) { System.err.println( "Error deleting files.\n" + e); } finally { try{ stream.close(); } // close the stream catch(IOException e) { System.err.println( e ); } } } // end deleteFilesInDir() /** Returns the file size of the sent file in bytes */ private long getFileSizeInBytes(Path path) { try { return Files.size(path); } catch (IOException e) { System.err.println( "Error getting file size.\n" + e ); } return 0; } } // end CopyEngine